# Loop

The Loop block is a container that executes blocks repeatedly. Iterate over collections, repeat operations a fixed number of times, or continue while a condition is met.

Loop blocks are container nodes that hold other blocks inside them. The contained blocks execute multiple times based on your configuration.

## Configuration Options

### Loop Type

Choose between four types of loops:

**For Loop**

**For Loop (Iterations)** - A numeric loop that executes a fixed number of times:

![For Loop with iterations](https://docs.steelengine.com/static/blocks/loop-1.png)

Use this when you need to repeat an operation a specific number of times.

```
Example: Run 5 times
- Iteration 1
- Iteration 2
- Iteration 3
- Iteration 4
- Iteration 5
```

**For Each**

**For Each (Collection)** - A collection-based loop that iterates over each item in an array or object:

![For Each Loop with collection](https://docs.steelengine.com/static/blocks/loop-2.png)

Use this when you need to process a collection of items.

```
Example: Process ["apple", "banana", "orange"]
- Iteration 1: Process "apple"
- Iteration 2: Process "banana"
- Iteration 3: Process "orange"
```

**While Loop**

**While Loop (Condition-based)** - Continues executing while a condition evaluates to true:

![While Loop with condition](https://docs.steelengine.com/static/blocks/loop-3.png)

Use this when you need to loop until a specific condition is met. The condition is checked **before** each iteration.

```
Example: While {"<variable.i>"} < 10
- Check condition → Execute if true
- Inside loop: Function returns {"<variable.i>"} + 1
- Inside loop: Variables assigns i = {"<function.result>"}
- Check condition → Execute if true
- Check condition → Exit if false
```

**Do While Loop**

**Do While Loop (Condition-based)** - Executes at least once, then continues while a condition is true:

![Do While Loop with condition](https://docs.steelengine.com/static/blocks/loop-4.png)

Use this when you need to execute at least once, then loop until a condition is met. The condition is checked **after** each iteration.

```
Example: Do-while {"<variable.i>"} < 10
- Execute blocks
- Inside loop: Function returns {"<variable.i>"} + 1
- Inside loop: Variables assigns i = {"<function.result>"}
- Check condition → Continue if true
- Check condition → Exit if false
```

A Variables block substitutes references but does not evaluate expressions, so `i = <variable.i> + 1` fails with an invalid-number error. Compute the new value in a [Function block](/blocks/function) and assign its `result`.

### Condition Evaluation

While and Do While conditions are JavaScript expressions. Before each check, SteelEngine replaces every `<block.field>` and `<variable.name>` reference with its current value. Strings are quoted, numbers and booleans are inserted as-is, and objects are inserted as JSON. SteelEngine then evaluates the expression in an isolated sandbox with a 5-second timeout and coerces the result to a boolean.

```
<variable.i> < 10
<variable.status> !== "done"
<variable.remaining> > 0 && <variable.attempts> < 5
```

If the condition throws or times out, it evaluates to false and the loop exits. A While loop with no condition never runs. A Do While loop with no condition falls back to a fixed iteration count.

## How to Use Loops

### Creating a Loop

1. Drag a Loop block from the toolbar onto your canvas
2. Configure the loop type and parameters
3. Drag other blocks inside the loop container
4. Connect the blocks as needed

### Referencing Loop Data

There's an important distinction between referencing loop data from **inside** vs **outside** the loop:

**Inside the Loop**

**Inside the loop**, use `<loop.>` references to access the current iteration context:

* **`<loop.index>`**: Current iteration number (0-based)
* **`<loop.currentItem>`**: Current item being processed (forEach only)
* **`<loop.items>`**: Full collection being iterated (forEach only)

```
// Inside a Function block within the loop
const idx = <loop.index>;           // 0, 1, 2, ...
const item = <loop.currentItem>;    // Current item
```

`<loop.iteration>` and `<loop.item>` are accepted aliases for `<loop.index>` and `<loop.currentItem>`. You can also drill into the current item with a path, for example `<loop.currentItem.email>`.

These references are only available for blocks **inside** the loop container. They give you access to the current iteration's context.

**Outside the Loop**

**Outside the loop** (after it completes), reference the loop block by its name to access aggregated results:

* **`<LoopBlockName.results>`**: One array per iteration, in iteration order. Each array holds one settled cell per block that produced output in that iteration.

Each cell is either `{ status: 'fulfilled', value }`, where `value` is the block's output object, or `{ status: 'rejected', error }` when that block failed:

```
// If your loop block is named "Process Items"
const iterations = <processitems.results>;
// [
//   [ { status: 'fulfilled', value: { content: '...' } } ],   // iteration 0
//   [ { status: 'rejected',  error: 'Agent failed: ...' } ],   // iteration 1
//   [ { status: 'fulfilled', value: { content: '...' } } ],   // iteration 2
// ]

// Collect the fulfilled values only
const values = iterations
  .flat()
  .filter((cell) => cell.status === 'fulfilled')
  .map((cell) => cell.value);
```

After the loop completes, use the loop's block name (not `loop.`) to access the collected results. The block name is normalized (lowercase, no spaces). `results` is the only field you can reference on a Loop block. The execution log also shows `failures`, `successCount`, `failureCount`, and `partial` for the same run.

## Example Use Cases

**Processing API Results** - For Each loop processes customer records from an API

```
API (Fetch) → Loop (For Each) → Agent (Analyze) → Function (Store)
```

**Iterative Content Generation** - For loop generates multiple content variations

```
Loop (5x) → Agent (Generate) → Evaluator (Score) → Function (Select Best)
```

**Counter with While Loop** - While loop processes items with counter

```
Variables (i=0) → Loop (While i<10) → Agent (Process) → Function (i + 1) → Variables (i = function.result)
```

## Advanced Features

### Failure Handling

When a block inside the loop body fails, the loop does not stop. SteelEngine records the failure as a `rejected` cell for that iteration. It skips the blocks connected after the failed block for the rest of that iteration. The loop then continues with the next iteration (or the next condition check). After the loop exits, downstream blocks see the failure in `<blockname.results>` and can decide what to do with it.

Configuration errors are different. These fail the Loop block itself before any iteration runs:

* An empty or unresolvable collection
* A count above 1,000
* A collection larger than 1,000 items

### Limitations

Container blocks (Loops and Parallels) support nesting. You can place loops inside loops, parallels inside loops, and any combination of container blocks to build complex multi-dimensional workflows.

Each loop executes its own iterations sequentially. Separate loops inside different Parallel branches can make progress concurrently when nested parallel execution is enabled. See [Parallel limitations](/blocks/parallel#limitations).

## Inputs and Outputs

**Configuration**

<ul>
  <li>
    <strong>Loop Type</strong>: Choose between 'for', 'forEach', 'while', or 'doWhile'
  </li>

  <li>
    <strong>Iterations</strong>: Number of times to execute (for loops)
  </li>

  <li>
    <strong>Collection</strong>: Array or object to iterate over (forEach loops)
  </li>

  <li>
    <strong>Condition</strong>: Boolean expression to evaluate (while/do-while loops)
  </li>
</ul>

**Variables**

Available **inside** the loop only:

<ul>
  <li>
    <strong>{"<loop.index>"}</strong>: Current iteration number (0-based)
  </li>

  <li>
    <strong>{"<loop.currentItem>"}</strong>: Current item being processed (forEach only)
  </li>

  <li>
    <strong>{"<loop.items>"}</strong>: Full collection (forEach only)
  </li>
</ul>

**Results**

<ul>
  <li>
    <strong>{"<blockname.results>"}</strong>: One array of settled cells per iteration (accessed via block name)
  </li>

  <li>
    <strong>Structure</strong>: Results maintain iteration order. Each cell is <code>{"{ status: 'fulfilled', value }"}</code> or <code>{"{ status: 'rejected', error }"}</code>
  </li>

  <li>
    <strong>Access</strong>: Available in blocks after the loop completes
  </li>
</ul>

## Best Practices

* **Set reasonable limits**: Keep iteration counts reasonable to avoid long execution times
* **Use For Each for collections**: When processing arrays or objects, use For Each instead of For loops
* **Handle errors gracefully**: Consider adding error handling inside loops for robust workflows

## Common Questions

**What is the maximum number of iterations a loop can run?**

For loops (fixed count) and For Each loops are limited to 1,000 iterations/items. Asking for more does not cap the loop. Instead, the Loop block fails before running with an error such as 'For loop iterations (1500) exceeds maximum allowed (1000). Execution blocked.' While loops and Do While loops with a condition have no hard iteration cap — they run until the condition evaluates to false. Do While loops without a condition fall back to a fixed iteration count, which is subject to the same 1,000 limit. Always ensure your While/Do While conditions will eventually become false to avoid infinite loops.

**Do loops execute iterations in parallel or sequentially?**

Loops execute all iterations sequentially, one after another. If you need concurrent execution across items, use the Parallel block instead. You can also nest a Parallel block inside a Loop if you need both iteration patterns.

**How do I access the current item inside a For Each loop?**

Inside the loop, use <loop.currentItem> to get the current item being processed and <loop.index> for the zero-based iteration number. These references are only available to blocks placed inside the loop container — blocks outside the loop cannot access them.

**How do I access loop results after it finishes?**

After the loop completes, reference the loop block by its normalized name (lowercase, no spaces) using <blockname.results>. This returns one array per iteration, in order. Each array holds a settled cell per block that ran in that iteration, either { status: 'fulfilled', value } or { status: 'rejected', error }. For example, if your loop block is named 'Process Items', use <processitems.results>. Do not use <loop.> syntax outside the loop — that only works inside.

**What happens if a block inside the loop fails?**

The loop continues. The failure becomes a { status: 'rejected', error } cell in that iteration's results. The remaining blocks in that iteration are skipped, and the next iteration starts as normal. Only configuration problems — an empty or unresolvable collection, or more than 1,000 iterations or items — fail the Loop block itself.

**Can I increment a counter with a Variables block inside the loop?**

Not directly. Variables assignments substitute references but do not evaluate expressions. A value of <variable.i> + 1 becomes the text '0 + 1' and fails for a number variable. Put a Function block inside the loop that returns <variable.i> + 1, then assign <function.result> to the variable.

**Can I nest loops inside each other?**

Yes. Container blocks (Loops and Parallels) fully support nesting. You can place loops inside loops, parallels inside loops, loops inside parallels, and any combination. Each nested container maintains its own scope and iteration context independently.

**What is the difference between a While loop and a Do While loop?**

A While loop checks its condition before each iteration, so it may execute zero times if the condition is false initially. A Do While loop executes its body at least once, then checks the condition after each iteration to decide whether to continue. Use Do While when you need guaranteed first execution.

**What happens if a For Each loop receives an empty collection?**

If the For Each loop's collection is empty, the loop body is skipped entirely and the loop outputs an empty results array. The workflow continues normally to any blocks connected after the loop.
