BeClaude
Guide2026-05-05

Mastering Claude’s Stop Reasons: A Practical Guide for API Developers

Learn how to interpret and handle Claude API stop_reason values like end_turn, tool_use, and max_tokens. Includes code examples, empty response fixes, and best practices.

Quick Answer

This guide explains every Claude API stop_reason value, how to handle them in code, and how to prevent or recover from empty responses. You’ll learn practical patterns for end_turn, tool_use, max_tokens, and stop_sequence.

Claude APIstop_reasontool_useerror handlingMessages API

Introduction

When you call the Claude Messages API, every successful response includes a stop_reason field. This small but critical piece of data tells you why Claude stopped generating—whether it finished naturally, needs to call a tool, hit a token limit, or encountered a stop sequence. Misunderstanding or ignoring stop_reason can lead to broken workflows, incomplete answers, or silent failures.

In this guide, you’ll learn:

  • What each stop_reason value means
  • How to handle them in Python and TypeScript
  • How to prevent and recover from empty responses
  • Best practices for robust multi-turn conversations

The stop_reason Field

The stop_reason field appears in every successful response from the Messages API. It is not an error—it’s a signal that Claude finished its response generation for a specific reason.

Here’s a typical response structure:

{
  "id": "msg_01234",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Here's the answer to your question..."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

Stop Reason Values

end_turn

Meaning: Claude finished its response naturally. This is the most common stop reason and usually indicates a complete answer. How to handle: Process the response content as final.
from anthropic import Anthropic

client = Anthropic() response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] )

if response.stop_reason == "end_turn": print(response.content[0].text)

tool_use

Meaning: Claude wants to call a tool (function) you’ve defined. The response will contain one or more tool_use content blocks. How to handle: Extract the tool name and input, execute the tool, then send the result back in a new user message with a tool_result block.
if response.stop_reason == "tool_use":
    for block in response.content:
        if block.type == "tool_use":
            tool_name = block.name
            tool_input = block.input
            tool_id = block.id
            # Execute your tool logic here
            result = my_tool_function(tool_name, tool_input)
            # Append tool_result to conversation
            messages.append({
                "role": "user",
                "content": [{
                    "type": "tool_result",
                    "tool_use_id": tool_id,
                    "content": str(result)
                }]
            })

max_tokens

Meaning: Claude stopped because it reached the max_tokens limit you set. The response may be cut off mid-sentence. How to handle: This is not an error—it’s a signal that you should continue the conversation. Append a new user message like “Please continue” to let Claude finish.
if response.stop_reason == "max_tokens":
    messages.append({
        "role": "user",
        "content": "Please continue from where you left off."
    })
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=messages
    )

stop_sequence

Meaning: Claude encountered a custom stop sequence you defined in your API request (e.g., "\n\nHuman:" or "<END>"). How to handle: The response is complete up to the stop sequence. The stop_sequence field will contain the actual sequence that triggered the stop.
if response.stop_reason == "stop_sequence":
    print(f"Stopped at sequence: {response.stop_sequence}")
    # Process the content normally

Handling Empty Responses with end_turn

A common pitfall: Claude returns an empty response (2–3 tokens, no content) with stop_reason: "end_turn". This typically happens in tool-use workflows.

Why It Happens

  • Adding text after tool results: If you insert a text block immediately after a tool_result, Claude learns to expect that pattern and may end its turn early.
  • Resending Claude’s completed response: If you send back Claude’s own finished response without adding anything new, Claude sees it’s done and stays done.

How to Prevent Empty Responses

Incorrect pattern:
messages = [
    {"role": "user", "content": "Calculate 1234 + 5678"},
    {"role": "assistant", "content": [
        {"type": "tool_use", "id": "toolu_123", "name": "calculator", "input": {"operation": "add", "a": 1234, "b": 5678}}
    ]},
    {"role": "user", "content": [
        {"type": "tool_result", "tool_use_id": "toolu_123", "content": "6912"},
        {"type": "text", "text": "Here's the result"}  # ❌ Don't add text here
    ]}
]
Correct pattern:
messages = [
    {"role": "user", "content": "Calculate 1234 + 5678"},
    {"role": "assistant", "content": [
        {"type": "tool_use", "id": "toolu_123", "name": "calculator", "input": {"operation": "add", "a": 1234, "b": 5678}}
    ]},
    {"role": "user", "content": [
        {"type": "tool_result", "tool_use_id": "toolu_123", "content": "6912"}  # ✅ Just the tool_result
    ]}
]

Recovery Strategy

If you still get empty responses, do not retry with the same empty response—Claude will remain done. Instead, add a new user message with a continuation prompt:

def handle_empty_response(client, messages):
    response = client.messages.create(
        model="claude-opus-4-20250514",
        max_tokens=1024,
        messages=messages
    )
    
    if response.stop_reason == "end_turn" and not response.content:
        # ✅ Add a new user message
        messages.append({
            "role": "user",
            "content": "Please continue"
        })
        response = client.messages.create(
            model="claude-opus-4-20250514",
            max_tokens=1024,
            messages=messages
        )
    
    return response

Best Practices

  • Always check stop_reason – Don’t assume end_turn means success. Validate the content length and type.
  • Handle tool_use in a loop – Claude may call multiple tools in sequence. Keep the conversation going until you get end_turn.
  • Set reasonable max_tokens – If you expect long responses, set max_tokens high enough to avoid truncation.
  • Use stop_sequences for structured output – Define custom stop sequences like "\n\nNext:" to break long outputs into chunks.
  • Log stop_reason and stop_sequence – For debugging and monitoring, always log these fields alongside token usage.

Full Example: Multi-Turn Tool Loop

from anthropic import Anthropic

client = Anthropic() messages = [{"role": "user", "content": "What's the weather in Tokyo and London?"}]

while True: response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=messages ) if response.stop_reason == "tool_use": for block in response.content: if block.type == "tool_use": # Execute tool and append result result = get_weather(block.input["location"]) messages.append({"role": "user", "content": [ {"type": "tool_result", "tool_use_id": block.id, "content": result} ]}) elif response.stop_reason == "end_turn": print(response.content[0].text) break elif response.stop_reason == "max_tokens": messages.append({"role": "user", "content": "Please continue."}) else: # stop_sequence or unexpected break

Key Takeaways

  • end_turn means Claude finished naturally; tool_use means it wants to call a tool; max_tokens means it hit your limit; stop_sequence means it hit a custom stop.
  • Never add text blocks after tool_result – this is the most common cause of empty responses.
  • To recover from empty responses, add a new user message with “Please continue” rather than retrying the same input.
  • Always loop on tool_use until you get end_turn to handle multi-tool conversations.
  • Log stop_reason and stop_sequence for debugging and monitoring response behavior.