← Files ConductorARCHIVED FILE

skills/conductor/examples/do-while-loop.md

4 KB · Oct 3, 2026 · 06:32 UTC

↓ Download file

# Example: DO_WHILE Loop with Iteration Counter

Loop a body of tasks until a JavaScript condition returns false. The body sees an auto-managed `iteration` counter on the loop task's output.

## The self-reference pattern

The `loopCondition` is JavaScript. Variables in the script are read from the task's `inputParameters` via `$.varName`. To read the iteration counter, you must wire the loop task's *own output* back into its inputs:

```json
"inputParameters": {
  "value": "${workflow.input.count}",
  "loop_ref": "${loop_ref.output}"
}
```

This looks like a typo — referencing the task before it has output — but it's the canonical pattern. Conductor resolves `${loop_ref.output}` lazily at each iteration, exposing the running counter as `$.loop_ref['iteration']` inside the script.

## Workflow

See [workflows/do-while-loop.json](workflows/do-while-loop.json):

```json
{
  "name": "loop",
  "taskReferenceName": "loop_ref",
  "type": "DO_WHILE",
  "evaluatorType": "graaljs",
  "loopCondition": "(function(){ return $.loop_ref['iteration'] < $.value; })();",
  "loopOver": [
    {
      "name": "do_work",
      "taskReferenceName": "do_work",
      "type": "HTTP",
      "inputParameters": {
        "http_request": {
          "uri": "${workflow.input.endpoint}?page=${loop_ref.output.iteration}",
          "method": "GET"
        }
      }
    }
  ],
  "inputParameters": {
    "value": "${workflow.input.count}",
    "loop_ref": "${loop_ref.output}"
  }
}
```

Three details that are easy to miss:

1. **`evaluatorType: "graaljs"` at the top of the DO_WHILE task.** Older docs omit it, and without it the condition fails at runtime on recent clusters.
2. **IIFE form for `loopCondition`.** `(function(){ return <expr>; })()` returns a clean boolean. The older `if (...) { true; } else { false; }` statement form is fragile, and named functions or `return true; return false;` are red flags.
3. **`loop_ref` wired through `inputParameters`.** This is what makes `$.loop_ref['iteration']` accessible inside the condition. Forgetting it is the #1 cause of `Cannot read property ... from undefined` failures.

## Run

```bash
conductor workflow create examples/workflows/do-while-loop.json
conductor workflow start -w paginated_fetch -i '{"endpoint": "https://api.example.com/items", "count": 5}'
```

## Reading per-iteration output

Each iteration's task output is keyed by iteration number under the loop task:

```
${loop_ref.output.1.do_work.response.body}
${loop_ref.output.2.do_work.response.body}
...
```

The total iteration count is at `${loop_ref.output.iteration}` — **not** `${loop_ref.iteration}`. See [../references/template-resolution.md](../references/template-resolution.md) Pitfall 3.

## Scope inside `loopCondition`

Only these are accessible:

- `$.<key>` for every key declared in `inputParameters` (after `${...}` resolution).

These are **not** in scope and will throw `TypeError`:

- `$.workflow.input.*`
- `$.workflow.variables.*`
- Any other task's output you didn't plumb in via `inputParameters`.

If you need a workflow input or variable inside the condition, declare it as an input parameter on the DO_WHILE task:

```json
"inputParameters": {
  "loop_ref": "${loop_ref.output}",
  "max_iterations": "${workflow.input.max_iterations}",
  "stop_flag": "${workflow.variables.stop_flag}"
}
```

then reference `$.max_iterations`, `$.stop_flag` in the condition.

## Notes

- `iteration` is 1-indexed.
- Every variable referenced as `$.x` in the condition must appear as a key in `inputParameters`. Forgetting this gives a confusing `undefined` failure at runtime.
- For unbounded loops, **always** include an iteration cap in addition to any result-driven exit (`(function(){ return $.loop_ref['iteration'] < 100 && !$.loop_ref[$.loop_ref['iteration']].do_work.output.done; })();`). The optimization checklist flags unbounded loops as CRITICAL.
- See [../references/graaljs-gotchas.md](../references/graaljs-gotchas.md) for the full set of GraalJS pitfalls (Java-Map-backed proxies, `JSON.stringify` behavior, reserved-ish input names, etc.).

SHA-256: 447add338b63daaef521631bd81e043a9465afe73dc2bbc298e7795694a581bd