Skip to main content

The Agent Loop

A turn is everything that happens from your prompt to the final answer. Inside a turn, the model asks for tools. Claude Code runs them. The model reads the results. This repeats until the model answers without a tool call. This page explains each stage. It also shows where you can act.

The Turn at a Glance​

Then Claude Code runs the tool and sends the result back to the model.

The turn ends when the model answers without a tool call.

The Stages​

Sources: Hooks Reference: Hook Lifecycle and Mods Reference for the events. Source: the program for the rest.

StageStatusWhat happensWhere you can act
1. Input
Documented: An official docs page names this item.
Claude Code reads your text. A message you type during a turn joins at a safe point.UserPromptSubmit hook, prompt.submit mod event
2. Command expansion
Documented: An official docs page names this item.
A slash command or skill name turns into a prompt.UserPromptExpansion hook
3. Context pickup
Documented: An official docs page names this item.
Claude Code adds nested memory, @ mentions, and file-change notes.InstructionsLoaded hook, prompt.mention and prompt.attachment mod events
4. Message prep
Undocumented: Only the program shows this item. It can change without notice.
Large tool results become references. Claude Code looks up the facts of the model.PreModelSwitch and PostModelSwitch hooks
5. Auto compaction
Documented: An official docs page names this item.
Claude Code compacts the conversation if it is over the threshold. See Context and Compaction.PreCompact and PostCompact hooks
6. Attachments
Undocumented: Only the program shows this item. It can change without notice.
Claude Code adds reminders and notes about the mode.prompt.attachment mod event
7. Request build
Documented: An official docs page names this item.
Claude Code builds the system, tools, and messages parts. See Request and Cache.prompt.section, prompt.compose, and tool.describe mod events
8. Stream
Documented: An official docs page names this item.
Claude Code sends the request and reads the events, with retries and fallbacks. See Models.MessageDisplay hook
9. Tools
Documented: An official docs page names this item.
Each tool call passes the permission check and then runs.Tool hooks (next section)
10. Next round
Undocumented: Only the program shows this item. It can change without notice.
The tool results join the messages. The loop starts again at stage 3.PostToolBatch hook
11. Stop
Documented: An official docs page names this item.
With no tool call, the Stop hooks run. A hook that blocks sends its reason back to the model.Stop and StopFailure hooks

The mod events turn.start, turn.step, and turn.complete wrap the whole turn. See the Mod API pages in Extend.

The Tool Stage​

Sources: Hooks Reference and Environment Variables.

StepStatusWhat happens
Permission check
Documented: An official docs page names this item.
Your rules and the permission mode decide to allow, ask, or deny. See Permission Flow in this section.
PreToolUse hook
Documented: An official docs page names this item.
The hook can block or change the call.
PermissionRequest hook
Documented: An official docs page names this item.
The hook runs when a decision is needed.
Run
Documented: An official docs page names this item.
Read-only tools and subagents run in parallel, up to CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY (default 10). Other tools run one after another.
PostToolUse and PostToolUseFailure hooks
Documented: An official docs page names this item.
They run after each call.
PostToolBatch hook
Documented: An official docs page names this item.
It runs after a batch of parallel calls and before the next model call. It can stop the loop.

Recovery Inside the Loop​

The loop does not fail on every error. It tries to fix the problem and continue.

CaseStatusWhat Claude Code does
The output stops at max_tokens
Undocumented: Only the program shows this item. It can change without notice.
It adds a nudge message and asks again, a limited number of times.
The response is cut off
Undocumented: Only the program shows this item. It can change without notice.
It does the same.
A tool_use block has no valid call
Undocumented: Only the program shows this item. It can change without notice.
It drops the reply and asks once more. Then it ends with a message that the tool call could not be parsed.
The model sends only thinking
Undocumented: Only the program shows this item. It can change without notice.
It sends one nudge.
A safety refusal
Documented: An official docs page names this item.
It runs again on the fallback model of that category. See Models.
The context is too long
Documented: An official docs page names this item.
It lowers max_tokens or compacts.
Autocompact refills 3 times in a row
Documented: An official docs page names this item.
It ends the turn with an error that starts Autocompact is thrashing. See Troubleshooting in the official docs.
A Stop hook blocks
Documented: An official docs page names this item.
It sends the reason back to the model. After CLAUDE_CODE_STOP_HOOK_BLOCK_CAP blocks in a row (default 8), it ends the turn.
--max-turns is reached
Documented: An official docs page names this item.
It ends the run. See the CLI Reference in the official docs.

How a Turn Ends​

The loop returns a reason. Claude Code records it in telemetry. The chat does not show it. A result message of the Agent SDK and of --output-format stream-json carries it as terminal_reason. All reasons are

Undocumented: Only the program shows this item. It can change without notice.
, because only the Agent SDK pages name some of them.

reasonMeaning
completedThe turn ended in the normal way.
max_turnsThe turn reached the turn limit.
aborted_streaming, aborted_toolsYou stopped the turn (for example with Esc) while the model streamed or tools ran.
hook_stopped, stop_hook_preventedA hook stopped the turn.
tool_deferredA hook deferred a tool call.
tool_deferred_unavailableClaude Code could not resume a deferred tool call.
background_requestedThe turn moved to a background session.
blocking_limitThe context hit the blocked level. See Context and Compaction.
rapid_refill_breakerAutocompact refilled the context too fast.
model_error, api_error, image_errorThe API or an image failed.
malformed_tool_use_exhaustedThe model sent a tool call that could not be parsed, and the retries failed too. Since 2.1.307 Claude Code retries the call up to twice (release notes). Before, the turn ended after two bad calls in a row.
prompt_too_longThe prompt did not fit the context window.
budget_exhaustedThe run used all of its budget.
structured_output_retry_exhaustedThe model did not give valid structured output after the allowed retries.
turn_setup_failedClaude Code failed to set up the turn.

What You Can Change​

GoalHow
Check or edit every prompt before it runsA UserPromptSubmit hook.
Block or rewrite a tool callA PreToolUse hook or a permission rule.
Force the model to keep workingA Stop hook that blocks. Mind CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.
Limit tool parallelismSet CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY.
Limit the number of turnsUse --max-turns in print mode.
Rewrite a part of the system promptA mod listening to prompt.section or prompt.compose. See The System Prompt.
Change the compaction pointSee Context and Compaction.

Example​

This hook in .claude/settings.json runs a script before every Bash call. If the script exits with code 2, Claude Code blocks the call. The reason goes back to the model.

.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": ".claude/hooks/check-bash.sh" }
]
}
]
}
}