end_turn

API

Definition

A stop_reason value indicating Claude finished its response naturally without hitting a limit or requesting a tool. In agentic loops, this signals the loop should stop and the final response should be presented to the user.

Example Usage

if response.stop_reason == 'end_turn': break # Task complete, present response

In Depth

`end_turn`: The Natural Completion Signal

end_turn is the stop_reason value that means Claude finished generating its response on its own terms — no token limit was reached, no tool was requested, no stop string was hit, and no safety boundary was encountered. In a single-turn exchange, end_turn simply means you have a complete answer. In an agentic loop, it is the signal that the model considers the task done and the loop should exit.

The importance of end_turn is not just what it means — it is that you should only present the final response to the user, or mark the task complete, when you receive this value. Any other stop_reason means something else is happening: a tool needs to run, the output was truncated, the model refused, or the context is exhausted.

`end_turn` in a Tool-Use Loop

A well-structured agentic loop treats end_turn as the sole exit condition for successful completion:

import anthropic

client = anthropic.Anthropic()

messages = [{"role": "user", "content": user_task}]

while True:
    response = client.messages.create(
        model="claude-opus-4-8",
        max_tokens=4096,
        tools=tool_definitions,
        messages=messages
    )

    if response.stop_reason == "end_turn":
        # Task complete — extract text and break
        final_text = next(
            block.text for block in response.content
            if block.type == "text"
        )
        break

    elif response.stop_reason == "tool_use":
        # Execute each requested tool and collect results
        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                result = execute_tool(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result
                })
        # Append assistant turn and tool results, then loop
        messages.append({"role": "assistant", "content": response.content})
        messages.append({"role": "user", "content": tool_results})

    else:
        # max_tokens, refusal, model_context_window_exceeded, etc.
        raise RuntimeError(f"Unexpected stop: {response.stop_reason}")

Note that all tool_result blocks for a parallel tool call must go into a single user message — not separate messages per tool.

Why Not Check Content Instead?

A tempting shortcut is to check whether the response text contains a phrase like "I have completed" or "Done" as the loop exit condition. This is unreliable: Claude may use such phrases mid-task, and it may complete a task without them. stop_reason is a machine-readable, structured signal explicitly designed for loop control — use it.

Relationship to Other Stop Reasons

end_turn sits at the top of the priority hierarchy for loop exit logic. See stop_reason for the full set of values and when each is appropriate. In the context of the agentic loop lifecycle, end_turn marks the transition from the execution phase back to the presentation layer.

How It's Tested & Common Confusions

Exam questions typically present loop pseudocode and ask you to identify the correct exit condition. end_turn is always the answer for successful natural completion. You may also be tested on why checking response text content is a wrong alternative, or on what to do if the loop never receives end_turn (diagnose: either the model is always requesting tools, hitting max_tokens, or the loop has a continuation bug).

Frequently Asked Questions

Can `end_turn` appear when there are tool_use blocks in the response?

No. When the model requests a tool call, `stop_reason` is `tool_use`, not `end_turn`. The two values are mutually exclusive in a single response. `end_turn` means the assistant turn contained only text (or thinking) content — no pending tool calls.

What if my loop runs for many iterations but never reaches `end_turn`?

This usually indicates one of three problems: (1) the model keeps calling tools in a cycle because it hasn't received sufficient tool results to conclude — check that your tool results are correctly formatted with matching `tool_use_id` values; (2) you have a bug in the message accumulation logic and the model isn't seeing prior context; or (3) the task is genuinely unbounded and you need a max-iteration guard to avoid an infinite loop. Always add a maximum iteration count as a safety backstop alongside the `end_turn` check.

Is `end_turn` guaranteed when Claude thinks it has answered the question?

Yes — if the model generates a final text response without hitting any limit or triggering a tool call, `stop_reason` will be `end_turn`. This is the model's only way to signal completion. However, 'completion' is the model's own assessment; for task-critical applications, add application-level validation (e.g., a [validation loop](/glossary/validation-loop)) after receiving `end_turn` to confirm the output actually meets your requirements.