Claude Platform Docs
MessagesMit Claude entwickeln

Stop-Gründe und Fallback

Erfahre, was jeder stop_reason-Wert bedeutet und wie du Abschneidung, Tool-Nutzung, pausierte Turns und Ablehnungen in deiner Anwendung behandelst.

Jede Antwort der Messages API enthält ein Feld stop_reason, das dir mitteilt, warum Claude die Generierung beendet hat. Prüfe dieses Feld, um zu entscheiden, ob du die Antwort unverändert verwendest, die Konversation fortsetzt, es erneut versuchst oder auf ein anderes Modell zurückfällst.

Das vollständige Antwortschema findest du in der Messages-API-Referenz.

Kurzübersicht

WertWann er auftrittWas zu tun ist
end_turnClaude hat seine Antwort auf natürliche Weise beendet.Verwende die Antwort.
max_tokensDie Antwort hat dein max_tokens-Limit erreicht.Erhöhe max_tokens oder setze die Antwort fort.
stop_sequenceClaude hat eine deiner stop_sequences ausgegeben.Lies stop_sequence, um zu sehen, welche ausgelöst wurde.
tool_useClaude ruft ein Tool auf.Führe das Tool aus und gib das Ergebnis zurück. Ein Server-Tool-Aufruf, dem sein Ergebnisblock noch fehlt, wird in einer späteren Antwort abgeschlossen.
pause_turnEine Server-Tool-Schleife hat ihr Iterationslimit erreicht.Sende den Assistant-Inhalt zurück, um fortzufahren.
refusalClaude hat eine Antwort abgelehnt.Lies stop_details und versuche es erneut mit einem Fallback-Modell.
model_context_window_exceededDie Antwort hat das Kontextfenster des Modells gefüllt.Behandle die Antwort als abgeschnitten.

Das Feld stop_reason

Das Feld stop_reason ist Teil jeder erfolgreichen Antwort der Messages API. Anders als Fehler, die auf Probleme bei der Verarbeitung deiner Anfrage hinweisen, teilt dir stop_reason mit, warum Claude die Generierung seiner Antwort abgeschlossen hat.

Example response
{
  "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,
  "stop_details": null,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

Werte von stop_reason

end_turn

Der häufigste Stop-Grund. Zeigt an, dass Claude seine Antwort auf natürliche Weise beendet hat.

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
    # Verarbeite die vollständige Antwort
    for block in response.content:
        if block.type == "text":
            print(block.text)

max_tokens

Claude hat gestoppt, weil es das in deiner Anfrage angegebene max_tokens-Limit erreicht hat.

client = anthropic.Anthropic()
# Anfrage mit begrenzten Tokens
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=10,
    messages=[{"role": "user", "content": "Explain quantum physics"}],
)

if response.stop_reason == "max_tokens":
    # Antwort wurde abgeschnitten
    print("Response was cut off at token limit")
    # Erwäge eine weitere Anfrage, um fortzufahren

stop_sequence

Claude ist auf eine deiner benutzerdefinierten Stop-Sequenzen gestoßen.

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    stop_sequences=["END", "STOP"],
    messages=[{"role": "user", "content": "Generate text until you say END"}],
)

if response.stop_reason == "stop_sequence":
    print(f"Stopped at sequence: {response.stop_sequence}")

tool_use

Claude ruft ein Tool auf und erwartet, dass du es ausführst.

client = anthropic.Anthropic()
weather_tool = {
    "name": "get_weather",
    "description": "Get the current weather in a given location",
    "input_schema": {
        "type": "object",
        "properties": {
            "location": {"type": "string", "description": "City and state"},
        },
        "required": ["location"],
    },
}


def execute_tool(name, tool_input):
    """Execute a tool and return the result."""
    return f"Weather in {tool_input.get('location', 'unknown')}: 72°F"


response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[weather_tool],
    messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
)

if response.stop_reason == "tool_use":
    # Tool extrahieren und ausführen
    for block in response.content:
        if block.type == "tool_use":
            result = execute_tool(block.name, block.input)
            # Ergebnis für die finale Antwort an Claude zurückgeben

Eine tool_use-Antwort kann auch einen server_tool_use-Block enthalten, dessen id keinen passenden Ergebnisblock hat. Dieser Server-Tool-Aufruf ist nicht abgeschlossen, und diese Antwort enthält sein Ergebnis nicht. Im häufigsten Fall ruft Claude ein Server-Tool und eines deiner Client-Tools in derselben Gruppe paralleler Tool-Aufrufe auf: Die API kehrt zurück, ohne das Server-Tool auszuführen, damit du zuerst die Client-Tools ausführen kannst. Es gibt keine andere Markierung für diesen Zustand; erkenne ihn, indem du für die id jedes server_tool_use- oder mcp_tool_use-Blocks prüfst, ob ein passender Ergebnisblock vorhanden ist.

A mixed tool_use response
{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "server_tool_use",
      "id": "srvtoolu_01HxbWnMRmbWyMfUtJKC45rA",
      "name": "web_search",
      "input": { "query": "example article" }
    },
    {
      "type": "tool_use",
      "id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "name": "run_command",
      "input": { "command": "uname -a" }
    }
  ]
}

Die Fortsetzung ist eine User-Nachricht aus tool_result-Blöcken, einer für jeden tool_use-Block in der Antwort (siehe Tool-Aufrufe behandeln), mit zwei zusätzlichen Regeln: Diese Nachricht darf nichts außer den tool_result-Blöcken enthalten, und die Anfrage muss dasselbe tools-Array beibehalten. Eine Fortsetzungsanfrage, die das wartende Server-Tool nicht mehr definiert, schlägt mit einem 400 fehl, dessen Meldung mit but no `web_search` tool was provided endet. Die API hängt deine Ergebnisse an den noch offenen Assistant-Turn an, führt das aufgeschobene Server-Tool aus (bei pausierter Code-Ausführung setzt sie diese fort) und setzt den Turn fort. Bei einem Server-Tool, das Claude direkt aufgerufen hat, beginnt der content der nächsten Antwort mit dem Ergebnisblock, der die server_tool_use-id der vorherigen Antwort beantwortet.

The follow-up user message
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
    }
  ]
}

Wenn du in dieser User-Nachricht nach den tool_result-Blöcken irgendetwas hinzufügst, etwa Text, beendet das den Assistant-Turn; bei einem Server-Tool, das Claude direkt aufgerufen hat, schlägt die Anfrage dann mit einem 400 invalid_request_error fehl, der das nicht aufgelöste Server-Tool benennt:

`web_search` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_search_tool_result` block

Das Weglassen eines tool_result oder das Platzieren eines solchen nach anderem Inhalt schlägt stattdessen früher mit dem Standardfehler tool_use ids were found without tool_result blocks immediately after fehl. Um Claude weitere Eingaben zu geben, sende sie als separate User-Nachricht, nachdem der Turn abgeschlossen ist.

pause_turn

Wird zurückgegeben, wenn die serverseitige Sampling-Schleife ihr Iterationslimit erreicht, während sie Server-Tools wie die Websuche ausführt. Das Standardlimit beträgt 10 Iterationen pro Anfrage.

Wenn dies geschieht, kann die Antwort einen server_tool_use-Block ohne zugehörigen Ergebnisblock enthalten. Damit Claude die Verarbeitung abschließen kann, setze die Konversation fort, indem du die Antwort unverändert zurücksendest. Eine Antwort, die einen Client-tool_use-Block auf dich warten lässt, hat niemals den stop_reason pause_turn: Wenn Claude stoppt, um deine Tools aufzurufen, ist stop_reason tool_use, und du setzt sie fort, indem du die Client-tool_result-Blöcke anstelle der Antwort selbst sendest.

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=4096,
    tools=[{"type": "web_search_20250305", "name": "web_search"}],
    messages=[{"role": "user", "content": "Search for latest AI news"}],
)

if response.stop_reason == "pause_turn":
    # Setze die Unterhaltung fort, indem du die Antwort zurücksendest
    messages = [
        {"role": "user", "content": "Search for latest AI news"},
        {"role": "assistant", "content": response.content},
    ]
    continuation = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=4096,
        messages=messages,
        tools=[{"type": "web_search_20250305", "name": "web_search"}],
    )

refusal

Claude hat es abgelehnt, eine Antwort zu generieren. Sicherheitsklassifikatoren geben diesen Stop-Grund als normale HTTP-200-Antwort zurück, nicht als Fehler.

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "[Unsafe request]"}],
)

if response.stop_reason == "refusal":
    # Claude hat eine Antwort abgelehnt
    print("Claude was unable to process this request")
    # Formuliere die Anfrage ggf. um oder passe sie an

Bei einer Ablehnung identifiziert das Objekt stop_details die Richtlinienkategorie, die sie ausgelöst hat. Die Kategorien und die vollständige Form der Ablehnungsantwort werden unter Ablehnungen und Fallback behandelt. stop_details ist für alle Stop-Gründe außer refusal null.

Eine abgelehnte Anfrage an Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5 oder Claude Sonnet 5.5 kann in der Regel durch einen erneuten Versuch mit einem anderen Claude-Modell bedient werden. Ablehnungen und Fallback zeigt, wie du diesen erneuten Versuch einrichtest, serverseitig oder in deinem Client. Wenn du den erneuten Versuch ausgehend von Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5 oder Claude Sonnet 5.5 selbst aufbaust, erklärt Fallback-Gutschrift, wie du vermeidest, die Prompt-Cache-Kosten doppelt zu bezahlen.

model_context_window_exceeded

Claude hat gestoppt, weil es das Limit des „context window“ (Kontextfenster) des Modells erreicht hat. Dadurch kannst du die maximal möglichen Token anfordern, ohne die genaue Eingabegröße zu kennen.

# Anfrage mit maximaler Token-Anzahl, um so viel wie möglich zu erhalten
response = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=20000,  # Python SDK requires streaming for max_tokens above ~21k
    messages=[
        {"role": "user", "content": "Large input that uses most of context window..."}
    ],
)

if response.stop_reason == "model_context_window_exceeded":
    # Antwort hat das Limit des Kontextfensters vor max_tokens erreicht
    print("Response reached model's context window limit")
    # Die Antwort ist weiterhin gültig, wurde aber durch das Kontextfenster begrenzt

Best Practices für den Umgang mit Stop-Gründen

Prüfe immer stop_reason

Mache es dir zur Gewohnheit, den stop_reason in deiner Antwortverarbeitungslogik zu prüfen:

def handle_response(response):
    match response.stop_reason:
        case "tool_use":
            return handle_tool_use(response)
        case "max_tokens":
            return handle_truncation(response)
        case "model_context_window_exceeded":
            return handle_context_limit(response)
        case "pause_turn":
            return handle_pause(response)
        case "refusal":
            return handle_refusal(response)
        case _:
            # Behandle end_turn und andere Fälle
            return next(
                (block.text for block in response.content if block.type == "text"),
                "",
            )

Behandle abgeschnittene Antworten elegant

Wenn eine Antwort aufgrund von Token-Limits oder des Kontextfensters abgeschnitten wird, hänge einen Hinweis an, damit der Leser weiß, dass die Ausgabe unvollständig ist. Um stattdessen die Generierung dort fortzusetzen, wo die Antwort aufgehört hat, siehe Vollständige Antworten sicherstellen.

def handle_truncated_response(response):
    text = next((block.text for block in response.content if block.type == "text"), "")
    if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
        if response.stop_reason == "max_tokens":
            note = "[Response truncated due to max_tokens limit]"
        else:
            note = "[Response truncated due to context window limit]"
        return f"{text}\n\n{note}"
    return text

Implementiere Retry-Logik für pause_turn

Bei der Verwendung von Server-Tools kann die API pause_turn zurückgeben, wenn die serverseitige Sampling-Schleife ihr Iterationslimit (standardmäßig 10) erreicht. Behandle dies, indem du die Konversation fortsetzt:

def handle_server_tool_conversation(client, user_query, tools, max_continuations=5):
    """
    Handle server tool conversations that may require multiple continuations.

    The server runs a sampling loop when executing server tools. If the loop
    reaches its iteration limit, the API returns pause_turn. Continue the
    conversation by sending the response back to let Claude finish.
    """
    messages = [{"role": "user", "content": user_query}]

    for _ in range(max_continuations):
        response = client.messages.create(
            model="claude-opus-5-5",
            max_tokens=4096,
            messages=messages,
            tools=tools,
        )

        if response.stop_reason != "pause_turn":
            # Claude ist mit der Verarbeitung fertig – gib die finale Antwort zurück
            return response

        # pause_turn: Ersetze die gesamte Nachrichtenliste, damit die Rollen weiter abwechseln
        messages = [
            {"role": "user", "content": user_query},
            {"role": "assistant", "content": response.content},
        ]

    # Maximale Anzahl an Fortsetzungen erreicht – gib die letzte Antwort zurück
    return response

Stop-Gründe vs. Fehler

Es ist wichtig, zwischen stop_reason-Werten und tatsächlichen Fehlern zu unterscheiden:

Stop-Gründe (erfolgreiche Antworten)

  • Teil des Antwort-Bodys
  • Zeigen an, warum die Generierung normal gestoppt hat
  • Antwort enthält gültigen Inhalt

Fehler (fehlgeschlagene Anfragen)

  • HTTP-Statuscodes 4xx oder 5xx
  • Zeigen Fehler bei der Anfrageverarbeitung an
  • Antwort enthält Fehlerdetails
client = anthropic.Anthropic()

try:
    response = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello!"}],
    )

    # Erfolgreiche Antwort mit stop_reason verarbeiten
    if response.stop_reason == "max_tokens":
        print("Response was truncated")

except anthropic.APIStatusError as e:
    # Tatsächliche Fehler behandeln
    match e.status_code:
        case 429:
            print("Rate limit exceeded")
        case 500:
            print("Server error")

Überlegungen zum Streaming

Bei der Verwendung von Streaming ist stop_reason:

  • null im initialen message_start-Event
  • Im message_delta-Event enthalten
  • In keinem anderen Event enthalten
client = anthropic.Anthropic()

with client.messages.stream(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
) as stream:
    for event in stream:
        if event.type == "message_delta":
            stop_reason = event.delta.stop_reason
            if stop_reason:
                print(f"Stream ended with: {stop_reason}")

Häufige Muster

Umgang mit Tool-Use-Workflows

def complete_tool_workflow(client, user_query, tools):
    messages = [{"role": "user", "content": user_query}]

    while True:
        response = client.messages.create(
            model="claude-opus-5-5",
            max_tokens=1024,
            messages=messages,
            tools=tools,
        )

        if response.stop_reason == "tool_use":
            # Tools ausführen und fortfahren
            tool_results = execute_tools(response.content)
            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})
        else:
            # Endgültige Antwort
            return response

Vollständige Antworten sicherstellen

def get_complete_response(client, prompt, max_attempts=3):
    messages = [{"role": "user", "content": prompt}]
    full_response = ""

    for _ in range(max_attempts):
        response = client.messages.create(
            model="claude-opus-5-5", messages=messages, max_tokens=4096
        )

        full_response += next(
            (block.text for block in response.content if block.type == "text"), ""
        )

        if response.stop_reason != "max_tokens":
            break

        # Mach dort weiter, wo es aufgehört hat
        messages = [
            {"role": "user", "content": prompt},
            {"role": "assistant", "content": full_response},
            {"role": "user", "content": "Please continue from where you left off."},
        ]

    return full_response

Maximale Token erhalten, ohne die Eingabegröße zu kennen

Mit dem Stop-Grund model_context_window_exceeded kannst du die maximal möglichen Token anfordern, ohne die Eingabegröße zu berechnen:

def get_max_possible_tokens(client, prompt):
    """
    Get as many tokens as possible within the model's context window
    without needing to calculate input token count
    """
    response = client.beta.messages.create(
        model="claude-opus-5-5",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=20000,  # Python SDK requires streaming for max_tokens above ~21k
    )

    match response.stop_reason:
        case "model_context_window_exceeded":
            # Maximal mögliche Anzahl an Tokens für die Eingabegröße erhalten
            print(
                f"Generated {response.usage.output_tokens} tokens (context limit reached)"
            )
        case "max_tokens":
            # Genau die angeforderte Anzahl an Tokens erhalten
            print(
                f"Generated {response.usage.output_tokens} tokens (max_tokens reached)"
            )
        case _:
            # Natürlicher Abschluss
            print(
                f"Generated {response.usage.output_tokens} tokens (natural completion)"
            )

    return next((block.text for block in response.content if block.type == "text"), "")

Nächste Schritte

Versuche abgelehnte Anfragen auf einem Fallback-Modell erneut, serverseitig oder in deinem Client.

Lass das SDK die tool_use-Schleife, die Ergebnisformatierung und erneute Versuche für dich verwalten.

Lies stop_reason beim Streaming aus dem message_delta-Event.

Behandle 4xx- und 5xx-HTTP-Fehler, die sich von Stop-Gründen unterscheiden.

Was this page helpful?