Jede Messages-API-Antwort enthält ein stop_reason-Feld, das dir mitteilt, warum Claude die Generierung beendet hat. Prü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-Content zurück, um fortzufahren. |
refusal | Claude hat die Antwort verweigert. | 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.
Claude hat gestoppt, weil es das in deiner Anfrage angegebene max_tokens-Limit erreicht hat.
Claude ist auf eine deiner benutzerdefinierten Stop-Sequenzen gestoßen.
Claude ruft ein Tool auf und erwartet, dass du es ausführst.
Eine 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.
{
"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 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_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.
{
"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_search` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_search_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 die Websuche 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.
Claude hat die Generierung einer Antwort verweigert. Sicherheitsklassifikatoren geben diesen Stop-Reason als normale HTTP-200-Antwort zurück, nicht als Fehler.
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 oder Claude Opus 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 serverseitig oder in deinem Client einrichtest. 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.
Mache es dir zur Gewohnheit, stop_reason in deiner Antwortverarbeitungslogik zu prüfen:
Wenn 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.
Bei 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:
Es ist wichtig, zwischen stop_reason-Werten und tatsächlichen Fehlern zu unterscheiden:
Bei der Verwendung von Streaming ist stop_reason:
null im initialen message_start-Eventmessage_delta-Event bereitgestelltMit dem Stop-Reason model_context_window_exceeded kannst du die maximal möglichen Token anfordern, ohne die Eingabegröße zu berechnen:
Wiederhole abgelehnte Anfragen mit einem Fallback-Modell, 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?
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-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)client = anthropic.Anthropic()
# Anfrage mit begrenzten Tokens
response = client.messages.create(
model="claude-opus-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 fortzufahrenclient = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-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}")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",
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ückresponse = client.messages.create(
model="claude-opus-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 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-5",
max_tokens=4096,
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
)client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
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 anzupassen# Anfrage mit maximalen Tokens, um so viel wie möglich zu erhalten
response = client.beta.messages.create(
model="claude-opus-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 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 begrenztdef 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 next(
(block.text for block in response.content if block.type == "text"), ""
)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 textdef 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", 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 responseclient = anthropic.Anthropic()
try:
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
# Behandle erfolgreiche Antwort mit stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIStatusError as e:
# Behandle tatsächliche Fehler
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")client = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-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}")def complete_tool_workflow(client, user_query, tools):
messages = [{"role": "user", "content": user_query}]
while True:
response = client.messages.create(
model="claude-opus-5", 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-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
# 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_responsedef 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",
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 next((block.text for block in response.content if block.type == "text"), "")