coleam00/Archon · error
Loop node '${node.id}' exceeded max iterations (${String(loo
Error message
Loop node '${node.id}' exceeded max iterations (${String(loop.max_iterations)}) ${describeUnmetCompletion(loop)} What it means
A loop (while/until) node (packages/workflows/src/dag-executor.ts:7000) exhausted loop.max_iterations without its until condition being satisfied. The executor logs 'loop_node.max_iterations_reached', notifies the conversation, and fails the node via failLoopNode with the last iteration output, iteration count, and maxIterations data.
Source
Thrown at packages/workflows/src/dag-executor.ts:7000
// in multi-node workflows. Resume correctness relies on the 'paused' DB status, not
// on the node's output state.
return {
state: 'completed',
output: lastIterationOutput,
costUsd: loopTotalCostUsd,
...(loopTotalTokens !== undefined ? { tokens: loopTotalTokens } : {}),
loopIterations: i,
};
}
}
// Max iterations exceeded
const errorMsg = `Loop node '${node.id}' exceeded max iterations (${String(loop.max_iterations)}) ${describeUnmetCompletion(loop)}`;
getLog().warn(
{ nodeId: node.id, maxIterations: loop.max_iterations, signal: loop.until },
'loop_node.max_iterations_reached'
);
await safeSendMessage(platform, conversationId, errorMsg, msgContext);
return failLoopNode(errorMsg, {
output: lastIterationOutput,
costUsd: loopTotalCostUsd,
...(loopTotalTokens !== undefined ? { tokens: loopTotalTokens } : {}),
loopIterations: loop.max_iterations,
data: { maxIterations: loop.max_iterations },
});
}
/**
* Pause the run for a human/system gate — the single persist path for all five
* suspend sites (`loop_group`, `loop`, `approval`, `workflow:` child, and the
* container write-back gate). By default, tolerates a lost CAS when the run
* was externally transitioned while the gate was being raised — e.g. a killed
* CLI's signal cleanup marked the run failed mid-pause (#1123), or an operator
* cancelled it from another surface. `pauseWorkflowRun`'s UPDATE only matches
* status='running'; when it misses, re-read the status: any non-running status
* means the pause lost a legitimate external race — log, skip theView on GitHub (pinned to 0773b97458)
Solutions
- Check the message's describeUnmetCompletion suffix for why the loop signal was never met
- Increase max_iterations on the loop node
- Verify the body node's structured output actually contains the field referenced by until/while
- Add exit criteria or a break condition inside the body so pathological runs terminate early with a clear error
Example fix
# workflow yaml
# before
max_iterations: 5
until: "${steps.review.approved == true}"
# after
max_iterations: 15
until: "${steps.review.approved == true}" # ensure review node outputs approved as boolean Defensive patterns
Strategy: validation
Validate before calling
// Prove the loop signal can be satisfied before looping:
const probe = await runBodyOnce(loop);
if (typeof probe[signalField] !== 'boolean') {
throw new Error(`loop signal field '${signalField}' missing or not boolean in body output`);
} Type guard
function isLoopResult(r: { state: string; error?: string; data?: { maxIterations?: number } }): r is { state: 'failed'; error: string; data: { maxIterations: number } } {
return r.state === 'failed' && r.error?.includes('exceeded max iterations') === true;
} Try / catch
try {
const r = await runLoopNode(loop);
if (r.state === 'failed' && r.error?.startsWith('Loop node')) {
console.error(`${r.error} (ran ${r.data.maxIterations} iterations, output=${JSON.stringify(r.output)})`);
}
} catch (e) { /* escalate; do not auto-retry an exhausted loop */ } Prevention
- Use structured output on the loop body so the until field is reliable
- Set max_iterations above the realistic convergence count
- Reference the correct output key in until/while expressions after any renames
- Monitor loop iterations via run telemetry to catch near-limit runs
When it happens
Trigger: A loop node whose until/while condition — typically evaluated over a prompt node's structured output — never evaluates true within max_iterations, e.g. the model never emits the sentinel field the signal checks, or the task needs more passes than configured.
Common situations: Agent refine loops where the AI keeps saying 'not done' in the wrong field; max_iterations too low for the convergence needed; a signal expression referencing a renamed output key; retriable external calls failing every pass.
Related errors
- Loop-group node '${node.id}' exceeded max iterations (${Stri
- Loop-group node '${node.id}' failed at iteration ${String(i)
- ❌ **Composed fan-out failed** (node `${node.id}`): ${current
- node_loop_interactive_deprecated
- node_loop_until_deprecated
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/c01a66ea0b4028b1.
Report an issue: GitHub.