The agentic loop has exactly one reliable control mechanism: stop_reason. Everything else — text content checks, iteration limits, natural language parsing — is either an anti-pattern or a safety net. Getting this wrong causes infinite loops, premature termination, or both.
The two states that drive the loop
stop_reason: "tool_use"→ the model wants tools executed. Continue the loop: execute the requested tools, append results, send the next API request.stop_reason: "end_turn"→ the model is done. Terminate the loop: return the final response to the user.
That’s the core control flow. The model decides when to continue (by requesting tools) and when to stop (by ending its turn). The loop’s job is to respect these signals.
The text content trap
The single most common anti-pattern: checking whether the response contains text content and using that as a completion signal. This breaks because text blocks and tool_use blocks can coexist in the same response. The model often includes explanatory text alongside tool calls — “I’ll now check the billing records…” followed by a tool_use block. Terminating on text presence kills the agent mid-task.
The reverse is also broken: if you use text presence to continue the loop, it never stops, because every response (including the final summary) contains text.
Iteration limits: safety net, not primary control
Using a hard iteration limit as the primary termination mechanism is an anti-pattern. Production data shows what happens: at a limit of 20, complex tasks requiring 25+ tool calls get terminated incomplete. The 7% premature-stop rate in one customer support system represented real customer issues left unresolved.
The correct role for iteration limits: a generous safety net (e.g., 100) that catches genuine infinite loops without interfering with normal operation. Normal termination comes from stop_reason: "end_turn".
Handling all six stop_reason values
Production systems encounter more than just tool_use and end_turn:
| stop_reason | Meaning | Action |
|---|---|---|
tool_use | Model wants tools executed | Continue loop, execute tools |
end_turn | Model completed naturally | Terminate normally |
max_tokens | Response was truncated | Terminate with truncation warning |
pause_turn | Turn was paused mid-work | Resume in a follow-up request |
refusal | Policy violation | Terminate with policy alert |
stop_sequence | Hit a stop sequence | Terminate normally |
Each needs distinct handling. Treating all non-tool_use values as end_turn silently drops truncation warnings, ignores resumable pauses, and hides policy violations. Type-specific handling catches all three issues.
The CI dual-constraint pattern
CI pipelines face two competing requirements: the agent must complete fully (premature termination misses bugs) AND must respect a wall-clock timeout (CI jobs can’t run forever). The solution: use stop_reason as primary control with a wall-clock timeout as the hard backstop. Most runs complete via end_turn well within time limits. When the timeout triggers, the agent gracefully terminates with partial findings rather than being hard-killed.
One-liner: Use stop_reason as the sole loop control (tool_use = continue, end_turn = stop), handle all six values distinctly, and relegate iteration limits to a generous safety net — never primary control.