Invoking workflows with empty arguments don't get default values assigned


Invoke a workflow WF1 from another WF2, import variables, but don’t assign any value to them.
You would expect default values from WF2 to get assigned, but they do not. Instead default type values is used for Int and Bool (String works as expected)
What i’m really confused about is: Why is it working for Strings, and not for Int and Bool?

I have not tested with other types.

My Guess:
I assume this bug is because when UiPath translates to VB, there is no way to call a function with an argument that don’t have a value, so what happens is that UiPath creates a new variable (that then will have a default value) and then put that through the invoke. The function on the other side then see a variable that have values and then don’t assign the default values,

A possible solution to this is making your invoke function check weather an argument is used or not, and if it isn’t used, the argument is removed from the call when translate to VB code.

Steps to reproduce:

See attached WFs.
invokeMe.xaml (6.2 KB)
Main.xaml (6.1 KB)

  • Run main and see output pane

Current Behavior:

In 3. invoke: when arguments are imported but not assigned any value, default type values get assigned for Int (0) and Bool (false), but not String ("").

Expected Behavior:

When invoking workflows with empty arguments, default arguments from the workflow being invoked should be assigned. This is the optimal and logical behavior.
All type should be assigned their default type value (currently Int and Bool does, but String does not, making them different). This is not optimal, but probably easier to make for you.

Studio/Robot/Orchestrator Version:


1 Like

Close, but not completely (I think… not that WF is the clearest thing ever…).
Workflow Foundation invokes use Dictionary<string, Argument> for passing arguments (also ArgumentCollection, but ti’s almost the same thing). AFAIK, it’s never translated to VB, on runtime it’s clr.
When you invoke, it creates a key for each argument listed (each name) and adds it to the dictionary. The value for that key is set by creating a concrete instance of an Argument (i.e. InArgument<string>).
From what I can tell by browsing the implementation of InArgument<T>, it creates an instance with location to the value which is an allocated space for it. For value types (int, bool, but not string, datatable etc.) there is no null, so when that location is read, it will read it as 0/false.
When a recipient activity (workflow is an activity itself) tries to fetch values, from what I was able to find it defaults to default value if, and only if, the result of fetching the value location from the input dictionary is null - this can be if there is no key in the dictionary or if the assigned Argument returns a null pointer for the location (so a passed reference type variable that has a null alue will still override the default, because the argument’s value is the variable).
I’m definitely missing a lot of steps in-between, but was the investigations result.

So to summarize:
Default values are used only if the passed value for that argument name evaluates to null based solely on what is in the passed Arguments dictionary. Due to that to make a value type argument use default, it has to be completely absent from that dictionary.

If that’s not clear - don’t stress, it’s a reference redirection issue and that’s bloody complicated (I might’ve gotten something wrong myself, wouldn’t be surprised).

From what I can tell, this is WF implementation thing, not UiPath related.

Not sure how this would work exactly (see above about the references). What if I do want to pass a null? Interestingly, if in your last invoke check you pass Nothing as the string’s value, it will use the default, which reinforces that is pointer location that matters (a variable with null value is still a memory address, Nothing is not) for if a default is used, not what’s inside of it.


Actually scratch that.
UiPath’s InvokeWorkflowFile does not work like a standard WF invoker with respect to arguments passing, so while above might still hold true, I can’t tell if it is or not.
The conclusion still seems relevant, but why is a different topic.

This also makes setting defaults for value types extremely brittle - it truely has to be absent from the invoke itself, otherwise you won’t even be able to check even if you move away from defaults to guard statements… One more reason to the long list of “why you shouldn’t rely on Default values”, I guess…