Sub Flow
Calls another saved flow as a function.
This is the node that turns a collection of flows into a maintainable system. Everything about building large automation depends on using it well.
Purpose
Runs a saved flow inline against the shared execution context, optionally passing arguments, then branches on the status the child returned.
When to use it
- A sequence of steps is used by more than one flow.
- A flow has grown past what you can read on one screen.
- You want to test a piece of logic in isolation.
- A routine needs to behave differently per caller, via arguments.
When not to use it
- For two nodes used once. The indirection costs more than it saves.
- To call itself. Recursion is rejected at run time via the live call stack.
- When the child needs to write back many values — a child communicates through its status, not by returning a payload.
Requirements
A saved flow to call. The child should declare any values it needs as Flow Input Parameters.
How it runs
The child runs to its own Stop or Return. That node's declared status is the child's return value, and it selects which port the parent leaves through.
Self-reference and circular calls are rejected at run time by the live call stack — the schema cannot know a flow's own id, so this is a runtime guard plus a UI exclusion.
Settings
| Setting | Type | Range | Notes |
|---|---|---|---|
| Flow | flow ref | — | Required. The saved flow to call |
| Flow name | string | ≤ 200 chars | Display cache only — calls bind to the flow id |
| Inputs | list | — | Argument mappings: a child input name, and a value template |
Argument passing
Each input mapping binds one of the child's declared Flow Inputs to a variable-aware template evaluated in the parent's scope:
| Child input | Value (evaluated in the parent) |
|---|---|
max_per_trip | {{max_per_trip}} |
slot_index | 5 |
target_name | {{current_target}} |
The evaluated values are seeded as locals of the child — true function-argument passing. The child has no knowledge of its caller, so the same child works from a queue entry, a schedule, or three different parents.
An empty or absent list means no mapping, which is valid; the child then uses its own declared defaults.
Renaming is safe
Calls bind to the flow id. Renaming the child never breaks a caller — flowName is only a display cache.
Ports
| Port | Taken when | Required |
|---|---|---|
| Success | The child finalized with success | Yes |
| Failure | The child finalized with failure | No |
| Cancelled | The child finalized with cancelled | No — never followed |
| Fatal | The child could not be run, or recursion was detected | No — never followed |
Basic example
Extract a reusable routine:
- Flow: Return to base
- Inputs: none
Wire Success → carry on, Failure → recovery.
Realistic example — a thin orchestrator
The cleanest large-flow shape is a small top-level flow that does nothing but call one worker with parameters:
The orchestrator declares the Flow Inputs the operator fills in, then passes them down. Changing the strategy means editing the worker; changing the parameters means editing nothing at all.
Advanced example — a library of small functions
Decompose a large automation into single-purpose children, each with two endings:
| Child flow | Inputs | Success means | Failure means |
|---|---|---|---|
Return To Base | — | We are at base | Could not get there |
Open Target List | — | List is open | Could not open it |
Process One Target | target_index | Handled it | Skipped or unreachable |
Check Capacity | — | Room available | Full |
The parent becomes readable at a glance:
Each child is independently testable, and the parent reads like a description of the process.
Best practices
- Give every child exactly the endings its caller needs — usually one success and one failure, each with a message.
- Declare inputs on the child, bind them at the call site. Never hard-code a value inside a child that a caller might want to vary.
- Keep a child to one job you can name in a sentence.
- Test children in isolation before wiring them into a parent.
- Watch for repeated calls to the same child on many branches. If five paths all call "Return to base", consider restructuring so the call happens once.
- Name children as actions —
Return To Base,Process One Target. - Remember
maxNodeExecutionscounts child nodes too. A parent calling a 20-node child 50 times executes 1000+ nodes.
Performance notes
- The child runs inline in the same session — there is no process boundary and no parallelism. Total time is the parent's time plus every child's time.
- Calling a child in a loop multiplies its full cost per iteration. Detection-heavy children are where this bites.
- Sub Flow adds negligible overhead of its own; the cost is entirely the child's body.
- Because everything is one session, a slow child blocks the whole run — decomposition is for maintainability, not speed.
Memory notes
- The child shares the run's execution context. Bound inputs are seeded as child locals.
- Every node the child executes is recorded in the run's
nodeResults, so deep call trees in long loops grow the context steadily. run-scoped Runtime Memory belongs to the run, so a child sees the same run memory as its parent.
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
| Expecting parallelism | The child runs inline, sequentially | Use multiple instances for concurrency |
| Child has no failure ending | The parent cannot detect problems | Give it a failure Stop with a message |
| Hard-coded values inside a child | Not reusable | Declare inputs, bind at the call site |
| Calling a flow that calls back | Rejected at run time | Break the cycle |
Ignoring maxNodeExecutions | Run dies mid-way | Raise the guardrail |
| Extracting two nodes into a child | More indirection than value | Keep it inline |
Troubleshooting
The parent always takes Success even when the child clearly failed.
The child's terminal node declares success. Check which Stop/Return it reached.
The child does not see its argument. The value is bound to a name the child does not declare as a Flow Input. Names must match exactly.
The node routes Fatal. The flow could not be run, or recursion was detected.
The run dies with MAX_NODE_EXECUTIONS_EXCEEDED.
Child nodes count. Multiply the child's size by how often it is called.
Renaming a child broke nothing — is that right? Yes. Calls bind to the flow id.
FAQ
Does the child run in parallel with the parent? No — inline and sequential in the same session.
Can a child change the parent's variables? It receives bound inputs as its own locals. Communicate results back through its terminal status, or a shared memory scope.
Can a flow call itself? No — recursion is rejected at run time.
How deep can calls nest?
Bounded in practice by maxNodeExecutions and the runtime's call-stack guard.
Is a Sub Flow's Failure port required? Only Success is required, but wiring Failure is strongly recommended.
Related nodes
- Stop / Return — define the child's return value
- Start — every child has its own
- Loop — calling a child repeatedly
- Variable — preparing argument values
Related pages
- Variables and Memory — declaring inputs
- Sub Flows — the authoring guide
- Execution Model