FlowsGuide

Loops

A Loop node runs the steps inside it once per item: once per element of a list, once per row a query returned, or — in While mode — until a stop condition is true. Inside the loop, steps read the current element as item; after it, the next step gets every iteration’s result, in the order of the input list.

What you will learn
  • Point a loop at a list, an expression or a SQL step’s rows
  • Bind body steps to item, item fields and index
  • Choose Sequential, Parallel (with how many at once) or While
  • Stop a loop early, and read a loop run iteration by iteration

How a loop runs

On the canvas a loop is a frame: the loop node on top, its body inside, an end cap at the bottom. When the run reaches it, the loop works out its list, then walks the body once per element — each iteration on its own copy of the state, so two iterations running at the same time never overwrite each other’s outputs. When every iteration is done, the flow continues under the cap.

A SQL step feeds a loop that sends one e-mail per row; the counter climbs as iterations finish.

The loop’s output is a list with one entry per iteration: the output of the body’s last step in that iteration. Results keep the order of the input list, whichever iteration finishes first.

Choose what to loop over

  1. Add the loop

    Click a + and pick Loop (For each item). The frame arrives empty; its inner + adds steps to the body.

  2. Set its Items

    In the loop’s panel, Items has three sources: Static value (a JSON list you type, such as [3, 1, 2]), From Node (a list in an earlier step’s output, or Start’s) and Expression (JavaScript that returns a list).

  3. Fill the body

    Add the steps to repeat inside the frame and bind their inputs to the current element (next section).

Bound From Node to a SQL step, the loop goes through the query’s rows on its own — you point at the step, not at rows — and the panel lists the columns each row has: Each row: id · email · First Name. An expression can shape the list first: nodes.fetchOrders.output.orders.filter((o) => o.total > 0).

Using the item inside the body

A step in the body binds an input From node → the loop, then picks what it needs from the current iteration:

BindingReads
itemThe whole current element.
item.emailA field of it — a column, for a SQL row.
item["First Name"]A field whose name has spaces or symbols.
indexThe position, from 0.
greet_row.py
def main(email: str, first_name: str, position: int = 0):
    return {"to": email, "greeting": f"Hi {first_name}!", "position": position}

Bound as email ← item.email, first_name ← item["First Name"] and position ← index, the step runs once per row. A row that lacks a field is data, not a mistake: that input is left unset, so its default applies — and if it is required, the step fails naming the iteration, the item of iteration 4 has no field 'email'.

Nested loops

In a loop inside a loop, item is the inner loop’s element. To reach the outer one, bind From node → the outer loop → item.name, or, in an expression, read loops.<loopId>.item and loops.<loopId>.index. Expressions in a body also see iter — the same pair as { value, index }; the editor reminds you with // in this loop: item, index, iter.

Sequential, parallel or while

The loop’s Execution setting decides how iterations are scheduled:

  • Sequential (default) — one iteration at a time, in order. Use it when each item depends on the previous one, or when a service must not be called twice at once.
  • Parallel — up to At once iterations run together, at most 20 (Capped at 20 iterations at once.). They run in waves; results still land in input order.
  • While — no list: the body repeats with item = 0, 1, 2… until Stop the loop when is true, At most 100 times by default (up to 1,000). Without a stop condition the run fails.

Stop the loop when

Stop the loop when is an optional expression checked after each iteration, in index order, with result (that iteration’s output), item and index. The first time it is true the loop ends there, and its output keeps the iterations up to and including that one. In a parallel loop, siblings of the same wave may already have run; their results are dropped, so the output never depends on how many ran at once.

JavaScriptstop-when.js
result.found === true        // stop at the first match
index >= 9                    // at most ten iterations

When an iteration fails

A failing iteration fails the loop: the iterations already running finish, no new wave starts, and the failure reported is the first by index — the same one every time, however the timing fell. A step with continue on error in its run settings lets the other iterations carry on instead.

Reading a loop run

The loop header tells its own story while it runs and after: 120 items · 5 at a time, 37 of 120 done, then the outcome counts — 118 ok, 2 failed, skipped, from cache. When something failed, a pill jumps between failures: Next failure · #41 · 1 of 2.

  • The arrows and the picker on the loop node choose which iteration the canvas shows — every step in the body repaints with that iteration’s status, inputs and output.
  • The loop’s Iterations tab is a table — Item, Status, Duration — with a Failed only filter. A long loop loads its rows as you scroll (Loading this iteration…).
  • The run’s Timeline draws one lane per iteration running at the same time, so a parallel loop shows its waves.

Limits and rules

  • Parallel loops run at most 20 iterations at once; a higher value is capped.
  • While loops repeat at most 1,000 times (100 by default) and need Stop the loop when.
  • A loop’s list must be a list: binding it to an object or a number fails the loop.
  • A loop over a result too large to pass between steps reads its rows from storage, page by page (large results).