> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anyreach.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Loop step

> Repeat a group of steps once per item, a fixed number of times, or until a condition changes.

The Loop step runs a group of steps repeatedly — once for each item in a list, a set number of times, or for as long as a condition holds. It is a **container**: the steps that repeat live inside it on the canvas, and everything after the loop connects to its bottom edge.

Iterations run one after another, never in parallel. Each one finishes before the next begins, so a later iteration can read what earlier ones produced. When the items are independent and you want them at the same time, use the [Parallel step](/workflows/steps/parallel) instead.

## Setting it up

Drop a Loop node on the canvas and it appears as an empty container with a **+** inside it. That **+** adds the first step of the body; the handle on the container's bottom edge is where the workflow continues once the loop is done.

The inspector has one required choice — **Repeat** — and the field below it changes to match.

| Repeat                      | What you supply                                   | Runs                           |
| --------------------------- | ------------------------------------------------- | ------------------------------ |
| **For each item in a list** | A list expression, e.g. `{{ fetch_users.users }}` | Once per item                  |
| **A fixed number of times** | A number, or an expression returning one          | That many times                |
| **While a condition holds** | A condition checked *before* each iteration       | Until the condition goes false |

An empty list runs zero iterations and the workflow carries on — that is not an error.

### Optional settings

**Stop early when** — a condition checked *after* every iteration. When it is true the loop ends. The iteration that satisfied it still counts and still contributes its result.

**Maximum iterations** — a ceiling. While-loops default to `100` if you leave it blank, since a condition that never goes false would otherwise run forever. Every mode is capped at `10,000` regardless.

**If a step inside the loop fails** —

| Option                           | Behavior                                                                                  |
| -------------------------------- | ----------------------------------------------------------------------------------------- |
| **Stop the whole run** (default) | The run fails, as any failing step would                                                  |
| **Record it and keep going**     | That iteration's result is `null`, the failure is recorded, and the next iteration starts |

## Referencing data

Inside the loop body:

| Expression         | Value                                                                                    |
| ------------------ | ---------------------------------------------------------------------------------------- |
| `{{ n3.item }}`    | The current item. In *fixed number* mode this is the index; in *while* mode it is `null` |
| `{{ n3.index }}`   | Iteration number, counting from **0**                                                    |
| `{{ n3.total }}`   | How many iterations there will be — `null` in *while* mode, which cannot know            |
| `{{ n3.results }}` | Every iteration finished **so far**                                                      |
| `{{ n3.errors }}`  | Failures recorded so far, under *Record it and keep going*                               |

After the loop:

| Expression            | Value                                      |
| --------------------- | ------------------------------------------ |
| `{{ n3.results }}`    | One entry per iteration, in order          |
| `{{ n3.iterations }}` | How many ran                               |
| `{{ n3.errors }}`     | Each entry is `{ "index": …, "error": … }` |

`n3` stands for the loop's own step id — replace it with yours. The inspector prints the right one for you.

<Note>
  To test how many results you have so far, use `{{ $count(n3.results) }}`, not `$exists()`. JSONata treats an empty array as nothing, so `$exists()` on a loop that has not finished an iteration yet will mislead you.
</Note>

### An iteration's result is its last step's output

Each entry in `results` is whatever the **final** step of the body produced. If your body ends with a Wait step, you will get the wait's output rather than the useful work before it — put the step whose output you want last.

## Looking back at earlier iterations

Because `results` is readable from inside the body, a step can use everything that came before it, not just the previous pass:

```
{{ $count(n3.results) = 0 ? 'first one' : 'seen ' & $count(n3.results) & ' already' }}
```

Building a running total works the same way:

```
{{ ($count(n3.results) = 0 ? 0 : n3.results[-1].total) + order.amount }}
```

<Warning>
  The run context is shared between iterations, so a step's output from the previous pass is still there when the next one starts. That is useful, but it means a step reading another step of the same body will silently pick up a **stale** value rather than failing if that step has not run yet this iteration. Guard with `$exists()` where it matters.
</Warning>

## Waiting inside a loop

A [Wait step](/workflows/steps/wait) inside the body suspends the whole run and resumes on the next iteration exactly where it left off — the item being processed and everything collected so far survive the pause. That makes *while* mode plus a Wait the way to poll something until it is ready:

1. **Wait** — pause between checks
2. **HTTP API** — ask for the current status
3. Loop condition — `{{ check_status.state != 'done' }}`

Run this asynchronously. A synchronous run only serves waits up to 5 seconds each and 15 seconds in total, and fails past that — which a poll loop reaches almost immediately.

## Nesting

A loop can contain another loop. The inner one runs completely on each pass of the outer one, and starts collecting fresh results each time. In the outer loop's `results`, each entry is the inner loop's summary — its `results`, `iterations`, and `errors`.

## When to use it

| Use Loop when                                 | Don't use it when                                                                          |
| --------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Doing the same thing to every item in a list  | You just need one item — use an expression to pick it                                      |
| Retrying until something succeeds, with a cap | You want a schedule — use a [Timer trigger](/workflows/triggers/scheduled)                 |
| Polling until an external job finishes        | The items are independent and the time adds up — use [Parallel](/workflows/steps/parallel) |
| Building a result from many steps in sequence | You only need to reshape data — use a [Transform step](/workflows/steps/transform)         |

## Considerations

* **Iterations are serial.** Ten items each taking two seconds takes twenty seconds. For concurrency, use the [Parallel step](/workflows/steps/parallel).
* **Body step data holds the last iteration only.** After the loop, a body step's own output is whatever the final pass left. The full history is in `results`.
* **The body must be self-contained.** A step inside the loop cannot also be reachable from after it; the workflow will not save. Steps that repeat go in the container, steps that run once go after it.
* **Output steps cannot go inside a loop.** An Output step ends the workflow, which is ambiguous mid-iteration. Put it after the loop.
* **A loop with no steps inside does nothing** and the workflow continues — it will not fail the run.
