Jede Messages-API-Antwort enthält ein stop_reason-Feld, das dir mitteilt, warum Claude die Generierung beendet hat. Überprüfe dieses Feld, um zu entscheiden, ob du die Antwort unverändert verwenden, die Konversation fortsetzen, es erneut versuchen oder auf ein anderes Modell zurückgreifen solltest.
Das vollständige Antwortschema findest du in der Messages-API-Referenz.
| Wert | Wann es auftritt | Was zu tun ist |
|---|---|---|
end_turn | Claude hat seine Antwort auf natürliche Weise beendet. | Verwende die Antwort. |
max_tokens | Die Antwort hat dein max_tokens-Limit erreicht. | Erhöhe max_tokens oder setze die Antwort fort. |
stop_sequence | Claude hat eine deiner stop_sequences ausgegeben. | Lies stop_sequence, um zu sehen, welche ausgelöst wurde. |
tool_use | Claude ruft ein Tool auf. | Führe das Tool aus und gib das Ergebnis zurück. Ein Server-Tool-Aufruf, dem noch sein Ergebnisblock fehlt, wird in einer späteren Antwort abgeschlossen. |
pause_turn | Eine Server-Tool-Schleife hat ihr Iterationslimit erreicht. | Sende den Assistant-Inhalt zurück, um fortzufahren. |
refusal | Claude hat die Antwort abgelehnt. | Lies stop_details und versuche es mit einem Fallback-Modell erneut. |
model_context_window_exceeded | Die Antwort hat das Kontextfenster des Modells gefüllt. | Behandle die Antwort als abgeschnitten. |
Das stop_reason-Feld ist Teil jeder erfolgreichen Messages-API-Antwort. Im Gegensatz zu Fehlern, die auf Probleme bei der Verarbeitung deiner Anfrage hinweisen, teilt dir stop_reason mit, warum Claude die Generierung seiner Antwort abgeschlossen hat.
{
"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
}
}Der häufigste Stop-Reason. Zeigt an, dass Claude seine Antwort auf natürliche Weise beendet hat.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
# Verarbeite die vollständige Antwort
print(response.content[0].text)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-4-8",
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 fortzufahrenClaude ist auf eine deiner benutzerdefinierten Stop-Sequenzen gestoßen.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-8",
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}")Claude ruft ein Tool auf und erwartet, dass du es ausführst.
Für die meisten Tool-Use-Implementierungen verwende den Tool Runner, der die Tool-Ausführung, Ergebnisformatierung und Konversationsverwaltung automatisch übernimmt.
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-4-8",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
)
if response.stop_reason == "tool_use":
# Extrahiere und führe das Tool aus
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
# Gib das Ergebnis an Claude für die finale Antwort zurückEine tool_use-Antwort kann auch einen server_tool_use-Block enthalten, dessen id keinen passenden Ergebnisblock hat. Dieser Server-Tool-Aufruf ist noch 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 keinen anderen Marker für diesen Zustand; erkenne ihn, indem du für jede id eines server_tool_use- oder mcp_tool_use-Blocks prüfst, ob ein passender Ergebnisblock vorhanden ist.
Bei programmatischen Tool-Aufrufen bedeutet dieselbe Antwortstruktur etwas anderes. Der Client-tool_use-Block stammt von Code, der im code_execution-Tool läuft, und nicht direkt von Claude, und sein caller-Feld benennt den code_execution-Block, der ihn aufgerufen hat. Dieser Code wurde bereits gestartet: Er ist pausiert und wartet auf deine tool_result-Blöcke, und das Senden dieser Blöcke setzt die Ausführung fort, anstatt ein aufgeschobenes Tool zu starten. Der eigene Ergebnisblock des code_execution-Blocks kommt an, sobald der Code fertig ist, was mehr als eine Runde von Tool-Ergebnissen dauern kann. Die nachfolgende User-Nachricht selbst ist in beiden Fällen gleich; bei programmatischen Tool-Aufrufen gib zusätzlich die id aus dem container-Feld der Antwort zurück, wie auf jener Seite gezeigt.
{
"stop_reason": "tool_use",
"content": [
{
"type": "server_tool_use",
"id": "srvtoolu_01HxbWnMRmbWyMfUtJKC45rA",
"name": "web_fetch",
"input": { "url": "https://example.com/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 verarbeiten), 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-Fehler fehl, dessen Meldung mit but no `web_fetch` 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.
{
"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 nach den tool_result-Blöcken in dieser User-Nachricht etwas hinzufügst, etwa Text, wird der Assistant-Turn beendet; 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_fetch` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_fetch_tool_result` blockDas Weglassen eines tool_result oder das Platzieren eines solchen nach anderem Inhalt schlägt 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.
Wird zurückgegeben, wenn die serverseitige Sampling-Schleife ihr Iterationslimit erreicht, während sie Server-Tools wie Websuche oder Web-Fetch ausführt. Das Standardlimit beträgt 10 Iterationen pro Anfrage.
Wenn dies passiert, kann die Antwort einen server_tool_use-Block ohne entsprechenden Ergebnisblock enthalten. Um Claude die Verarbeitung abschließen zu lassen, 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 einen stop_reason von pause_turn: Wenn Claude stoppt, um deine Tools aufzurufen, ist stop_reason tool_use, und du setzt fort, indem du die Client-tool_result-Blöcke sendest statt der Antwort selbst.
response = client.messages.create(
model="claude-opus-4-8",
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 Konversation 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-4-8",
max_tokens=4096,
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
)Deine Anwendung sollte pause_turn in jeder Agent-Schleife behandeln, die Server-Tools verwendet. Füge die Antwort des Assistants zu deinem Messages-Array hinzu und stelle eine weitere API-Anfrage, damit Claude fortfahren kann.
Claude hat die Generierung einer Antwort abgelehnt. Bei Claude Fable 5 geben Sicherheitsklassifikatoren diesen Stop-Reason als normale HTTP-200-Antwort zurück, nicht als Fehler.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}],
)
if response.stop_reason == "refusal":
# Claude hat die Antwort abgelehnt
print("Claude was unable to process this request")
# Erwäge, die Anfrage umzuformulieren oder anzupassenWenn du bei der Verwendung von Claude Sonnet 4.5 oder Opus 4.1 (veraltet) häufig auf refusal-Stop-Reasons stößt, kannst du versuchen, deine API-Aufrufe auf Haiku 4.5 (claude-haiku-4-5-20251001) umzustellen, das andere Nutzungseinschränkungen hat. Erfahre mehr über das Verständnis der API-Sicherheitsfilter von Sonnet 4.5.
Bei einer Ablehnung identifiziert das stop_details-Objekt die Richtlinienkategorie, die sie ausgelöst hat. Die Kategorien und die vollständige Struktur der Ablehnungsantwort werden unter Ablehnungen und Fallback behandelt. stop_details ist null für alle Stop-Reasons außer refusal.
Eine abgelehnte Anfrage bei Claude Fable 5 kann in der Regel durch einen erneuten Versuch mit einem anderen Claude-Modell bedient werden, und Ablehnungen und Fallback zeigt, wie du diesen Retry einrichtest, serverseitig oder in deinem Client. Fallback-Guthaben erklärt, wie du vermeidest, die Prompt-Cache-Kosten doppelt zu bezahlen, wenn du den Retry selbst implementierst.
Claude hat gestoppt, weil es das Limit des Kontextfensters des Modells erreicht hat. Dies ermöglicht es dir, die maximal möglichen Token anzufordern, ohne die genaue Eingabegröße zu kennen.
Dieser Stop-Reason ist derzeit nur im beta-Namespace der SDKs typisiert, daher rufen die folgenden Beispiele client.beta.messages auf und verwenden die mit Beta präfixierten Typen. Bei Sonnet 4.5 und neueren Modellen gibt die API diesen Wert ohne Beta-Header zurück. Für ältere Modelle füge den Beta-Header model-context-window-exceeded-2025-08-26 hinzu, um ihn zu aktivieren.
# Anfrage mit maximalen Tokens, um so viel wie möglich zu erhalten
response = client.beta.messages.create(
model="claude-opus-4-8",
max_tokens=20000, # Python SDK requires streaming for max_tokens above ~21k (Opus 4.8 supports 128k with streaming)
messages=[
{"role": "user", "content": "Large input that uses most of context window..."}
],
)
if response.stop_reason == "model_context_window_exceeded":
# Antwort hat das Kontextfenster-Limit vor max_tokens erreicht
print("Response reached model's context window limit")
# Die Antwort ist weiterhin gültig, wurde aber durch das Kontextfenster begrenztMache es dir zur Gewohnheit, stop_reason in deiner Antwortverarbeitungslogik zu überprüfen:
def handle_response(response):
if response.stop_reason == "tool_use":
return handle_tool_use(response)
elif response.stop_reason == "max_tokens":
return handle_truncation(response)
elif response.stop_reason == "model_context_window_exceeded":
return handle_context_limit(response)
elif response.stop_reason == "pause_turn":
return handle_pause(response)
elif response.stop_reason == "refusal":
return handle_refusal(response)
else:
# Behandle end_turn und andere Fälle
return response.content[0].textWenn eine Antwort aufgrund von Token-Limits oder des Kontextfensters abgeschnitten wird, füge einen Hinweis hinzu, 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):
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"{response.content[0].text}\n\n{note}"
return response.content[0].textBei der Verwendung von Server-Tools kann die API pause_turn zurückgeben, wenn die serverseitige Sampling-Schleife ihr Iterationslimit erreicht (Standard: 10). 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-4-8", max_tokens=4096, messages=messages, tools=tools
)
if response.stop_reason != "pause_turn":
# Claude hat die Verarbeitung abgeschlossen – gib die finale Antwort zurück
return response
# pause_turn: ersetze die gesamte Nachrichtenliste, um abwechselnde Rollen beizubehalten
messages = [
{"role": "user", "content": user_query},
{"role": "assistant", "content": response.content},
]
# Maximale Anzahl an Fortsetzungen erreicht – gib die letzte Antwort zurück
return responseEs ist wichtig, zwischen stop_reason-Werten und tatsächlichen Fehlern zu unterscheiden:
client = anthropic.Anthropic()
try:
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
# Verarbeite erfolgreiche Antwort mit stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIStatusError as e:
# Verarbeite tatsächliche Fehler
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")Bei der Verwendung von Streaming ist stop_reason:
null im initialen message_start-Eventmessage_delta-Event bereitgestelltclient = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-4-8",
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}")Einfacher mit dem Tool Runner: Das folgende Beispiel zeigt die manuelle Tool-Handhabung. Für die meisten Anwendungsfälle übernimmt der Tool Runner die Tool-Ausführung automatisch mit deutlich weniger Code.
def complete_tool_workflow(client, user_query, tools):
messages = [{"role": "user", "content": user_query}]
while True:
response = client.messages.create(
model="claude-opus-4-8", max_tokens=1024, messages=messages, tools=tools
)
if response.stop_reason == "tool_use":
# Führe Tools aus und fahre fort
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 responsedef 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-4-8", messages=messages, max_tokens=4096
)
full_response += response.content[0].text
if response.stop_reason != "max_tokens":
break
# Fahre dort fort, 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_responseMit dem Stop-Reason 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-4-8",
messages=[{"role": "user", "content": prompt}],
max_tokens=20000, # Python SDK requires streaming for max_tokens above ~21k
)
if response.stop_reason == "model_context_window_exceeded":
# Maximale mögliche Token-Anzahl bei gegebener Eingabegröße erhalten
print(
f"Generated {response.usage.output_tokens} tokens (context limit reached)"
)
elif response.stop_reason == "max_tokens":
# Genau die angeforderte Token-Anzahl erhalten
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# Natürlicher Abschluss
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return response.content[0].textVersuche abgelehnte Anfragen mit einem Fallback-Modell erneut, serverseitig oder in deinem Client.
Lass das SDK die tool_use-Schleife, Ergebnisformatierung und Retries für dich verwalten.
Lies stop_reason aus dem message_delta-Event beim Streaming.
Behandle 4xx- und 5xx-HTTP-Fehler, die sich von Stop-Reasons unterscheiden.
Was this page helpful?