Chaque réponse de l'API Messages inclut un champ stop_reason qui vous indique pourquoi Claude a arrêté de générer. Vérifiez ce champ pour décider s'il faut utiliser la réponse telle quelle, poursuivre la conversation, réessayer ou vous replier sur un autre modèle.
Pour le schéma complet de la réponse, consultez la référence de l'API Messages.
| Valeur | Quand cela se produit | Que faire |
|---|---|---|
end_turn | Claude a terminé sa réponse naturellement. | Utilisez la réponse. |
max_tokens | La réponse a atteint votre limite max_tokens. | Augmentez max_tokens ou poursuivez la réponse. |
stop_sequence | Claude a émis l'une de vos stop_sequences. | Lisez stop_sequence pour voir laquelle s'est déclenchée. |
tool_use | Claude appelle un outil. | Exécutez l'outil et renvoyez le résultat. Un appel d'outil serveur auquel il manque encore son bloc de résultat se termine dans une réponse ultérieure. |
pause_turn | Une boucle d'outils serveur a atteint sa limite d'itérations. | Renvoyez le contenu de l'assistant pour continuer. |
refusal | Claude a refusé de répondre. | Lisez stop_details et réessayez sur un modèle de repli. |
model_context_window_exceeded | La réponse a rempli la fenêtre de contexte du modèle. | Traitez la réponse comme tronquée. |
Le champ stop_reason fait partie de chaque réponse réussie de l'API Messages. Contrairement aux erreurs, qui indiquent des échecs dans le traitement de votre requête, stop_reason vous indique pourquoi Claude a terminé la génération de sa réponse.
{
"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
}
}La raison d'arrêt la plus courante. Indique que Claude a terminé sa réponse naturellement.
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":
# Traiter la réponse complète
for block in response.content:
if block.type == "text":
print(block.text)Claude s'est arrêté parce qu'il a atteint la limite max_tokens spécifiée dans votre requête.
client = anthropic.Anthropic()
# Requête avec un nombre limité de 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":
# La réponse a été tronquée
print("Response was cut off at token limit")
# Envisagez d'effectuer une autre requête pour continuerClaude a rencontré l'une de vos séquences d'arrêt personnalisées.
client = 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}")Claude appelle un outil et s'attend à ce que vous l'exécutiez.
Pour la plupart des implémentations d'utilisation d'outils, utilisez le tool runner, qui gère automatiquement l'exécution des outils, le formatage des résultats et la gestion de la conversation.
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":
# Extraire et exécuter l'outil
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
# Renvoyer le résultat à Claude pour la réponse finaleUne réponse tool_use peut également contenir un bloc server_tool_use dont l'id n'a pas de bloc de résultat correspondant. Cet appel d'outil serveur n'est pas terminé, et cette réponse ne contient pas son résultat. Dans le cas courant, Claude appelle un outil serveur et l'un de vos outils clients dans le même groupe d'appels d'outils parallèles : l'API renvoie sans exécuter l'outil serveur afin que vous puissiez d'abord exécuter les outils clients. Il n'y a pas d'autre marqueur pour cet état ; détectez-le en vérifiant pour chaque bloc server_tool_use ou mcp_tool_use si son id a un bloc de résultat correspondant.
Avec l'appel d'outils programmatique, la même forme de réponse signifie quelque chose de différent. Le bloc tool_use client provient du code qui s'exécute dans l'outil code_execution plutôt que de Claude directement, et son champ caller nomme le bloc code_execution qui l'a appelé. Ce code a déjà démarré : il est en pause en attendant vos blocs tool_result, et leur envoi reprend l'exécution au lieu de démarrer un outil différé. Le bloc de résultat propre au bloc code_execution arrive une fois que le code se termine, ce qui peut prendre plus d'un cycle de résultats d'outils. Le message utilisateur de suivi lui-même est le même dans les deux cas ; avec l'appel d'outils programmatique, renvoyez également l'id du champ container de la réponse, comme le montre cette page.
{
"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" }
}
]
}La continuation est un message utilisateur composé de blocs tool_result, un pour chaque bloc tool_use de la réponse (voir Gérer les appels d'outils), avec deux règles supplémentaires : ce message ne doit rien contenir d'autre que les blocs tool_result, et la requête doit conserver le même tableau tools. Une requête de reprise qui ne définit plus l'outil serveur en attente échoue avec une erreur 400 dont le message se termine par but no `web_search` tool was provided. L'API attache vos résultats au tour d'assistant encore ouvert, exécute l'outil serveur différé (pour une exécution de code en pause, la reprend) et poursuit le tour. Pour un outil serveur que Claude a appelé directement, le content de la réponse suivante commence par le bloc de résultat qui répond à l'id du server_tool_use de la réponse précédente.
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
"content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
}
]
}Ajouter quoi que ce soit après les blocs tool_result dans ce message utilisateur, comme du texte, met fin au tour de l'assistant ; pour un outil serveur que Claude a appelé directement, la requête échoue alors avec une erreur 400 invalid_request_error qui nomme l'outil serveur non résolu :
`web_search` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_search_tool_result` blockOmettre un tool_result, ou en placer un après d'autres contenus, échoue plus tôt avec l'erreur standard tool_use ids were found without tool_result blocks immediately after à la place. Pour donner plus d'entrées à Claude, envoyez-les dans un message utilisateur séparé une fois le tour terminé.
Renvoyé lorsque la boucle d'échantillonnage côté serveur atteint sa limite d'itérations pendant l'exécution d'outils serveur tels que la recherche web. La limite par défaut est de 10 itérations par requête.
Lorsque cela se produit, la réponse peut contenir un bloc server_tool_use sans bloc de résultat correspondant. Pour permettre à Claude de terminer le traitement, poursuivez la conversation en renvoyant la réponse telle quelle. Une réponse qui laisse un bloc tool_use client en attente de votre part n'a jamais un stop_reason de pause_turn : lorsque Claude s'arrête pour appeler vos outils, stop_reason est tool_use, et vous la poursuivez en envoyant les blocs tool_result clients au lieu de la réponse elle-même.
response = 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":
# Continuez la conversation en renvoyant la réponse
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"}],
)Votre application doit gérer pause_turn dans toute boucle d'agent qui utilise des outils serveur. Ajoutez la réponse de l'assistant à votre tableau de messages et effectuez une autre requête API pour permettre à Claude de continuer.
Claude a refusé de générer une réponse. Les classificateurs de sécurité renvoient cette raison d'arrêt comme une réponse HTTP 200 normale, et non comme une erreur.
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 a refusé de répondre
print("Claude was unable to process this request")
# Envisagez de reformuler ou de modifier la requêteSi vous rencontrez fréquemment des raisons d'arrêt refusal en utilisant Claude Sonnet 4.5 ou Opus 4.1 (obsolète ; voir Dépréciations de modèles), vous pouvez essayer de mettre à jour vos appels API pour utiliser Haiku 4.5 (claude-haiku-4-5-20251001), qui a des restrictions d'utilisation différentes. En savoir plus sur la compréhension des filtres de sécurité de l'API de Sonnet 4.5.
Lors d'un refus, l'objet stop_details identifie la catégorie de politique qui l'a déclenché. Les catégories et la forme complète de la réponse de refus sont couvertes dans Refus et repli. stop_details est null pour toutes les raisons d'arrêt autres que refusal.
Une requête refusée sur Claude Fable 5 ou Claude Opus 5 peut généralement être servie en réessayant sur un autre modèle Claude, et Refus et repli montre comment configurer cette nouvelle tentative, côté serveur ou dans votre client. Crédit de repli explique comment éviter de payer deux fois le coût de la mise en cache des prompts lorsque vous construisez vous-même la nouvelle tentative.
Claude s'est arrêté parce qu'il a atteint la limite de la fenêtre de contexte du modèle. Cela vous permet de demander le maximum de tokens possible sans connaître la taille exacte de l'entrée.
Cette raison d'arrêt n'est actuellement typée que dans l'espace de noms beta des SDK, donc les exemples suivants appellent client.beta.messages et utilisent les types préfixés par Beta. Sur Sonnet 4.5 et les modèles plus récents, l'API renvoie cette valeur sans en-tête bêta. Pour les modèles antérieurs, ajoutez l'en-tête bêta model-context-window-exceeded-2025-08-26 pour l'activer.
# Requête avec le maximum de tokens pour en obtenir le plus possible
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":
# La réponse a atteint la limite de la fenêtre de contexte avant max_tokens
print("Response reached model's context window limit")
# La réponse reste valide mais a été limitée par la fenêtre de contextePrenez l'habitude de vérifier le stop_reason dans votre logique de gestion des réponses :
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:
# Gérer end_turn et les autres cas
return next(
(block.text for block in response.content if block.type == "text"), ""
)Lorsqu'une réponse est tronquée en raison des limites de tokens ou de la fenêtre de contexte, ajoutez un avis afin que le lecteur sache que la sortie est incomplète. Pour continuer la génération là où la réponse s'est arrêtée, consultez plutôt Garantir des réponses complètes.
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 textLors de l'utilisation d'outils serveur, l'API peut renvoyer pause_turn si la boucle d'échantillonnage côté serveur atteint sa limite d'itérations (10 par défaut). Gérez cela en poursuivant la conversation :
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", max_tokens=4096, messages=messages, tools=tools
)
if response.stop_reason != "pause_turn":
# Claude a terminé le traitement - retourner la réponse finale
return response
# pause_turn : remplacer la liste complète de messages pour conserver l'alternance des rôles
messages = [
{"role": "user", "content": user_query},
{"role": "assistant", "content": response.content},
]
# Nombre max de continuations atteint - retourner la dernière réponse
return responseIl est important de distinguer les valeurs de stop_reason des erreurs réelles :
client = anthropic.Anthropic()
try:
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
# Gérer la réponse réussie avec stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIStatusError as e:
# Gérer les erreurs réelles
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")Lors de l'utilisation du streaming, stop_reason est :
null dans l'événement initial message_startmessage_deltaclient = 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}")Plus simple avec le tool runner : l'exemple suivant montre une gestion manuelle des outils. Pour la plupart des cas d'usage, le tool runner gère automatiquement l'exécution des outils avec beaucoup moins de code.
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":
# Exécuter les outils et continuer
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Réponse finale
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
# Continuer là où il s'est arrêté
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Please continue from where you left off."},
]
return full_responseAvec la raison d'arrêt model_context_window_exceeded, vous pouvez demander le maximum de tokens possible sans calculer la taille de l'entrée :
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",
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":
# Nombre maximal de tokens possible obtenu compte tenu de la taille de l'entrée
print(
f"Generated {response.usage.output_tokens} tokens (context limit reached)"
)
elif response.stop_reason == "max_tokens":
# Nombre exact de tokens demandés obtenu
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# Fin naturelle
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return next((block.text for block in response.content if block.type == "text"), "")Réessayez les requêtes refusées sur un modèle de repli, côté serveur ou dans votre client.
Laissez le SDK gérer pour vous la boucle tool_use, le formatage des résultats et les nouvelles tentatives.
Lisez stop_reason depuis l'événement message_delta lors du streaming.
Gérez les erreurs HTTP 4xx et 5xx, qui sont distinctes des raisons d'arrêt.
Was this page helpful?