Container Steps and scope v2
A container step allows steps to be nested within it.
The built-in control-flow containers include If, ForEach, and TryCatch.
Scope rules
An executable Step can reference:
- Workflow Parameters.
- Returns from earlier root Steps.
- Returns from earlier Steps in the same nested scope.
- Values explicitly exposed by an active parent scope, such as a
ForEachitem.
A nested Step cannot reference a later Step, a sibling branch, or a value that exists only inside a completed or unrelated scope. A Step outside a ForEach cannot reference its active Items row.
The Canvas enforces the same model when you create visual connections. It blocks a connection from a nested Step directly to an unrelated external or sibling scope. Map a result onto the owning container's supported Return contract when data must leave a scope.
Structural placeholders
If and TryCatch contain named placeholder Steps. A placeholder exists only to own its child Steps. It must omit Node, Method, Parameters, and Returns.
- Name: choosePath
Node: v2.If@latest
Method: If
Parameters:
Expression:
Type: boolean
Expression: "{.isPriority}"
Steps:
- Name: "true"
Steps: []
- Name: "false"
Steps: []
The compiler supplies empty required placeholders when a supported control-flow branch is omitted from authored YAML. Keep them visible in manually maintained source because they make the structure unambiguous and round-trip predictably to Canvas view.
If
An If Step requires a boolean Expression and two placeholders named exactly true and false.
Only the selected branch runs. Its sibling branch does not run and cannot supply data to later mappings. The If Step's Result is the string true or false, indicating which branch was selected.
- Name: choosePath
Node: v2.If@latest
Method: If
Parameters:
Expression:
Type: boolean
Expression: "{checkOrder.IsPriority}"
Steps:
- Name: "true"
Steps:
- Name: notifyPriorityTeam
Node: example.Notification@latest
Method: Send
Parameters: {}
Returns: {}
- Name: "false"
Steps: []
The branch placeholder name is structural and case-sensitive. Do not rename it or add Node configuration to it.
ForEach
A ForEach Step enumerates the collection mapped to its one required Items Parameter. Its executable child Steps appear directly under Steps; there is no branch placeholder.
Inside the loop, reference the active item through the loop Step name and Items:
{processOrders.Items.orderId}
The Items schema defines the nested fields that validation and autocomplete can resolve. The input mapping itself is evaluated before the active per-item scope exists, so it cannot reference {processOrders.Items...}.
- Name: processOrders
Node: v2.ForEach@latest
Method: ForEach
Parameters:
Items:
Type: array
Expression: "{listOrders.Orders}"
Properties:
orderId:
Type: string
Returns:
Result:
Type: array
Expression: "{processOrders.Items}"
Properties:
orderId:
Type: string
Expression: "{processOrders.Items.orderId}"
Steps:
- Name: processOrder
Node: example.Orders@latest
Method: Process
Parameters:
OrderId:
Type: string
Expression: "{processOrders.Items.orderId}"
Returns: {}
The runtime executes the child sequence once per item and accumulates the mapped Result. A mapped Return rooted on the active item represents one output item for that iteration; the outer Result remains an array.
Outside the loop, consume {processOrders.Result}. Do not reference {processOrders.Items...} because there is no current row after the iteration scope.
ForEach consumes its source lazily where possible. Re-reading an upstream deferred source inside each iteration can require replay retention, so avoid repeated external collection reads in the loop body. See Lazy Evaluation and Streaming.
TryCatch
A TryCatch Step contains placeholders named exactly Try, Catch, and Finally.
Tryruns first.Catchruns only when a Step inTryfails.Finallyruns after the normal or caught-error path and is intended for follow-up work that must be attempted in either case.
- Name: protectUpdate
Node: v2.TryCatch@latest
Method: TryCatch
Steps:
- Name: Try
Steps:
- Name: updateRecord
Node: example.Records@latest
Method: Update
Parameters: {}
Returns: {}
- Name: Catch
Steps:
- Name: recordFailure
Node: example.Diagnostics@latest
Method: Record
Parameters: {}
Returns: {}
- Name: Finally
Steps: []
When Try succeeds, the runtime skips Catch and continues to Finally. When Try fails, it discards the remaining Try frames, records a bounded error value for the handler, runs Catch, and then runs Finally. An unhandled failure in Catch or Finally continues outward to an enclosing handler or fails the Workflow.
You can connect Returns from Try or Catch to a Step in Finally in the same TryCatch. Direct mappings between Try and Catch are not allowed. Finally is identified by its structural name, not its position in the YAML. Handle a missing result when the producer did not run; see Error Handling.
Nesting containers
Containers can be nested. Each level adds another scope and execution condition. Use clear names and keep nesting shallow enough that a reader can understand the active row, branch, and error boundary.
When a nested section becomes independently reusable or has a stable input/output contract, extract it into a Sub-Workflow instead of adding more levels.
Canvas behavior
Canvas view renders structural placeholders as labeled sections rather than ordinary executable Step cards. You can add, move, or paste executable Steps into an allowed section. Collapsing a container hides its nested cards without changing execution.
Search and diagnostic navigation expand required ancestors to reveal a nested Step. Copying or moving a control-flow Step includes its subtree; branch placeholders remain attached to their owner.
See also
See Steps, Execution order, and Expression syntax.