All nodes/Logic & Flow/Iteration
While Loop
Repeats the loop body while the condition is true. The Start/End ports are pure exec (no data is passed). Done returns the original Input. The optional Status port explains the stop reason: condition_false or max_iterations.
Type in the graph: while_loop
ExecSubgraph
An error branch can be enabled (expose_error_output) to handle failures on their own path.
Ports can be split into separate execution and data handles.
Try it
Repeating with a ceiling
The condition is checked before every pass, the Max Iterations field guards against an endless loop, and the Status port explains the stop.
- Execute + Data
- Execute
Runs as pasted
When to use it
Reach for this node when the number of repetitions depends on a condition rather than on a list: poll a job until it is ready, retry until the answer passes a check, refine a model’s result until it is good enough. When the count is the length of a list, use for_each, map or filter instead. Building a loop by hand, with an edge from a branch back up the graph, does not work: the scheduler treats that graph as cyclic and refuses it.
How it works
input (Input) carries the run trigger and a value. start (Start) and end (End) bound
the body — they are pure exec ports and carry no data. completed (Done) fires once the
loop stops, handing on the value that arrived on input.
The condition is evaluated before every pass, including the first: if it is false right
away, the body never runs and completed still fires. The body has to change whatever the
condition reads, or the loop runs into the ceiling. Values travel between passes through
variables (a var_set node, Session scope) or through
{{ nodes.<id>.output }} of a body node.
Passes are bounded twice: by “Max Iterations” and by the engine ceiling, 100 by default. The
optional status (Status) port reports why the loop stopped — condition_false or
max_iterations — and the same value is available as {{ nodes.<id>.status }}. While the
loop runs, {{ variables.loop_index }} holds the current pass, counting from zero.
Common mistakes
- A condition in mixed form. As with if,
{{ counter }} < 5 and {{ flag }}is no longer a single comparison after substitution, and a non-empty string is truthy. Write the whole condition inside{{ }}. - A typo in a path gives zero iterations. A missing path renders empty, the condition counts as false, the body is marked “Inside a loop body that never iterated” and the run is reported as successful.
- A counter on
loop_indexis off by one. The variable does not exist at the first check, and later it holds the previous pass, soloop_index < 2gives three passes. For an exact count use “Max Iterations”. - Expecting data on the Start port — that edge carries the trigger only.
- A Thread-scope variable in the condition. Only Session-scope variables appear in
{{ variables.<name> }}; read a thread variable through its var node’s output. - A loop inside a loop is rejected by the validator.
- An empty condition will not deploy — the expression is required.
Inputs
| Port | Wire | Payload | Notes |
|---|---|---|---|
Inputinput | Execute + Dataexecute_data | — | Execution trigger and payload passed through to Done |
Endend | Executeexecute | — |
Outputs
| Port | Wire | Payload | Notes |
|---|---|---|---|
Resultcompleted | Execute + Dataexecute_data | — | |
Startstart | Executeexecute | — | |
Statusstatus | Datadata | — | shown when expose_status = true |
Configuration
| Field | Type | Default | Description |
|---|---|---|---|
Conditioncondition_expression | string | "" | Loop continues while this expression is truthy supports templates |
Expose Status portexpose_status | boolean | false | Show a Status data output port carrying the termination reason (condition_false or max_iterations) |
Max Iterationsmax_iterations | integer | null | Maximum number of iterations (null = unlimited, bounded by engine cap) |
Shared fields
Every node has these three — the platform adds them, not the node author.
expose_error_output— When enabled, show an execution output to connect nodes that run if this step fails.split_ports_in— Show separate execution and data input handles instead of one combined port.split_ports_out— Show separate execution and data output handles instead of one combined port.