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 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:
"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.
See workflows/do-while-loop.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:
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.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.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.conductor workflow create examples/workflows/do-while-loop.json
conductor workflow start -w paginated_fetch -i '{"endpoint": "https://api.example.com/items", "count": 5}'
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 Pitfall 3.
loopConditionOnly 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.*inputParameters.If you need a workflow input or variable inside the condition, declare it as an input parameter on the DO_WHILE task:
"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.
iteration is 1-indexed.$.x in the condition must appear as a key in inputParameters. Forgetting this gives a confusing undefined failure at runtime.(function(){ return $.loop_ref['iteration'] < 100 && !$.loop_ref[$.loop_ref['iteration']].do_work.output.done; })();). The optimization checklist flags unbounded loops as CRITICAL.JSON.stringify behavior, reserved-ish input names, etc.).