PrepGenAICerts

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.

PrepGenAICerts is an independent exam-prep site. It is not affiliated with, endorsed by, or sponsored by Anthropic, PBC. Claude and Anthropic are trademarks of Anthropic, PBC. Exam details are taken from publicly available Anthropic and Pearson documentation. About PrepGenAICerts.