Motivos de parada e fallback
Saiba o que cada valor de stop_reason significa e como lidar com truncamento, uso de ferramentas, turnos pausados e recusas na sua aplicação.
Toda resposta da Messages API inclui um campo stop_reason que informa por que Claude parou de gerar. Verifique esse campo para decidir se deve usar a resposta como está, continuar a conversa, tentar novamente ou fazer fallback para outro modelo.
Para o esquema completo da resposta, consulte a referência da Messages API.
Referência rápida
| Valor | Quando ocorre | O que fazer |
|---|---|---|
end_turn | Claude terminou sua resposta naturalmente. | Use a resposta. |
max_tokens | A resposta atingiu seu limite de max_tokens. | Aumente max_tokens ou continue a resposta. |
stop_sequence | Claude emitiu uma das suas stop_sequences. | Leia stop_sequence para ver qual foi acionada. |
tool_use | Claude está chamando uma ferramenta. | Execute a ferramenta e retorne o resultado. Uma chamada de ferramenta de servidor ainda sem seu bloco de resultado é concluída em uma resposta posterior. |
pause_turn | Um loop de ferramenta de servidor atingiu seu limite de iterações. | Envie o conteúdo do assistente de volta para continuar. |
refusal | Claude se recusou a responder. | Leia stop_details e tente novamente em um modelo de fallback. |
model_context_window_exceeded | A resposta preencheu a janela de contexto do modelo. | Trate a resposta como truncada. |
O campo stop_reason
O campo stop_reason faz parte de toda resposta bem-sucedida da Messages API. Diferentemente dos erros, que indicam falhas no processamento da sua requisição, stop_reason informa por que Claude concluiu a geração da resposta.
{
"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
}
}Valores de stop reason
end_turn
O motivo de parada mais comum. Indica que Claude terminou sua resposta naturalmente.
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":
# Processa a resposta completa
for block in response.content:
if block.type == "text":
print(block.text)Às vezes Claude retorna uma resposta vazia (exatamente 2–3 tokens sem conteúdo) com stop_reason: "end_turn". Isso normalmente ocorre quando Claude interpreta que o turno do assistente está completo, particularmente após resultados de ferramentas.
Causas comuns:
- Adicionar blocos de texto imediatamente após resultados de ferramentas (Claude aprende a esperar que o usuário sempre insira texto após resultados de ferramentas, então encerra seu turno para seguir o padrão)
- Enviar a resposta concluída de Claude de volta sem adicionar nada (Claude já determinou que terminou, então permanecerá assim)
Como evitar respostas vazias:
# INCORRETO: adicionar texto imediatamente após tool_result
messages = [
{"role": "user", "content": "Calculate the sum of 1234 and 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 after tool_result
},
],
},
]
# CORRETO: enviar os resultados das ferramentas diretamente, sem texto adicional
messages = [
{"role": "user", "content": "Calculate the sum of 1234 and 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, no additional text
]Se você ainda receber respostas vazias após corrigir a estrutura das mensagens, adicione um prompt de continuação em uma nova mensagem do usuário em vez de tentar novamente com a resposta vazia:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-opus-5-5", max_tokens=1024, messages=messages
)
# Verifica se a resposta está vazia
if response.stop_reason == "end_turn" and not response.content:
# INCORRETO: não tente novamente apenas com a resposta vazia
# Isso não vai funcionar porque o Claude já decidiu que terminou
# CORRETO: adicione um prompt de continuação em uma NOVA mensagem do usuário
messages.append({"role": "user", "content": "Please continue"})
response = client.messages.create(
model="claude-opus-5-5", max_tokens=1024, messages=messages
)
return responseMelhores práticas:
- Nunca adicione blocos de texto imediatamente após resultados de ferramentas: Isso ensina Claude a esperar entrada do usuário após cada uso de ferramentas.
- Não tente novamente respostas vazias sem modificação: Enviar a resposta vazia de volta não ajudará.
- Use prompts de continuação como último recurso: Somente se essas correções não resolverem o problema.
max_tokens
Claude parou porque atingiu o limite de max_tokens especificado na sua requisição.
client = anthropic.Anthropic()
# Requisição com tokens limitados
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":
# A resposta foi truncada
print("Response was cut off at token limit")
# Considere fazer outra requisição para continuarSe a resposta de Claude for cortada porque atingiu o limite de max_tokens, e a resposta truncada contiver um bloco de uso de ferramentas incompleto, você precisará repetir a requisição com um valor de max_tokens maior para obter o uso de ferramentas completo.
# Verifica se a resposta foi truncada durante o uso de ferramentas
if response.stop_reason == "max_tokens":
# Verifica se o último bloco de conteúdo é um tool_use incompleto
last_block = response.content[-1]
if last_block.type == "tool_use":
# Envia a requisição com um max_tokens maior
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=4096, # Increased limit
messages=messages,
tools=tools,
)stop_sequence
Claude encontrou uma das suas sequências de parada personalizadas.
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 está chamando uma ferramenta e espera que você a execute.
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":
# Extrai e executa a ferramenta
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
# Retorna o resultado ao Claude para a resposta finalUma resposta tool_use também pode conter um bloco server_tool_use cujo id não tem um bloco de resultado correspondente. Essa chamada de ferramenta de servidor não está concluída, e essa resposta não traz seu resultado. No caso comum, Claude chama uma ferramenta de servidor e uma das suas ferramentas de cliente no mesmo grupo de chamadas de ferramentas paralelas: a API retorna sem executar a ferramenta de servidor para que você possa executar as ferramentas de cliente primeiro. Não há outro marcador para esse estado; detecte-o verificando se o id de cada bloco server_tool_use ou mcp_tool_use tem um bloco de resultado correspondente.
{
"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" }
}
]
}A continuação é uma mensagem de usuário com blocos tool_result, um para cada bloco tool_use na resposta (consulte Lidar com chamadas de ferramentas), com duas regras extras: essa mensagem não deve conter nada além dos blocos tool_result, e a requisição deve manter o mesmo array tools. Uma requisição de retomada que não define mais a ferramenta de servidor em espera falha com um 400 cuja mensagem termina com but no `web_search` tool was provided. A API anexa seus resultados ao turno do assistente ainda aberto, executa a ferramenta de servidor adiada (para execução de código pausada, a retoma) e continua o turno. Para uma ferramenta de servidor que Claude chamou diretamente, o content da próxima resposta começa com o bloco de resultado que responde ao id do server_tool_use da resposta anterior.
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
"content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
}
]
}Adicionar qualquer coisa após os blocos tool_result nessa mensagem de usuário, como texto, encerra o turno do assistente; para uma ferramenta de servidor que Claude chamou diretamente, a requisição então falha com um 400 invalid_request_error que nomeia a ferramenta de servidor não resolvida:
`web_search` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_search_tool_result` blockOmitir um tool_result, ou colocar um após outro conteúdo, falha antes com o erro padrão tool_use ids were found without tool_result blocks immediately after. Para dar mais entrada a Claude, envie-a como uma mensagem de usuário separada após a conclusão do turno.
pause_turn
Retornado quando o loop de amostragem do lado do servidor atinge seu limite de iterações ao executar ferramentas de servidor, como a busca na web. O limite padrão é de 10 iterações por requisição.
Quando isso acontece, a resposta pode conter um bloco server_tool_use sem um bloco de resultado correspondente. Para permitir que Claude termine o processamento, continue a conversa enviando a resposta de volta como está. Uma resposta que deixa um bloco tool_use de cliente aguardando você nunca tem um stop_reason de pause_turn: quando Claude para a fim de chamar suas ferramentas, o stop_reason é tool_use, e você a continua enviando os blocos tool_result de cliente em vez da própria resposta.
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":
# Continue a conversa enviando a resposta de volta
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 se recusou a gerar uma resposta. Os classificadores de segurança retornam esse motivo de parada como uma resposta HTTP 200 normal, não como um erro.
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 se recusou a responder
print("Claude was unable to process this request")
# Considere reformular ou modificar a solicitaçãoEm uma recusa, o objeto stop_details identifica a categoria de política que a acionou. As categorias e o formato completo da resposta de recusa são abordados em Recusas e fallback. stop_details é null para todos os motivos de parada que não sejam refusal.
Uma requisição recusada no Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Sonnet 5.5 ou Claude Haiku 5.5 geralmente pode ser atendida tentando novamente em outro modelo Claude. Recusas e fallback mostra como configurar essa nova tentativa, no lado do servidor ou no seu cliente. O Claude Haiku 5.5 não tem fallback no lado do servidor, então configure a nova tentativa dele no seu cliente. Se você mesmo criar a nova tentativa a partir do Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5 ou Claude Sonnet 5.5, o crédito de fallback explica como evitar pagar duas vezes o custo do cache de prompt.
model_context_window_exceeded
Claude parou porque atingiu o limite da "context window" (janela de contexto) do modelo. Isso permite que você solicite o máximo possível de tokens sem saber o tamanho exato da entrada.
# Requisição com o máximo de tokens para obter o máximo possível
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":
# A resposta atingiu o limite da janela de contexto antes de max_tokens
print("Response reached model's context window limit")
# A resposta ainda é válida, mas foi limitada pela janela de contextoMelhores práticas para lidar com motivos de parada
Sempre verifique o stop_reason
Crie o hábito de verificar o stop_reason na sua lógica de tratamento de respostas:
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 _:
# Trata end_turn e outros casos
return next(
(block.text for block in response.content if block.type == "text"),
"",
)Lide com respostas truncadas de forma elegante
Quando uma resposta é truncada por causa de limites de tokens ou da janela de contexto, acrescente um aviso para que o leitor saiba que a saída está incompleta. Para, em vez disso, continuar gerando a partir de onde a resposta parou, consulte Garantindo respostas completas.
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 textImplemente lógica de nova tentativa para pause_turn
Ao usar ferramentas de servidor, a API pode retornar pause_turn se o loop de amostragem do lado do servidor atingir seu limite de iterações (padrão 10). Lide com isso continuando a conversa:
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 terminou o processamento - retorna a resposta final
return response
# pause_turn: substitui a lista completa de mensagens para manter a alternância de papéis
messages = [
{"role": "user", "content": user_query},
{"role": "assistant", "content": response.content},
]
# Limite máximo de continuações atingido - retorna a última resposta
return responseMotivos de parada vs. erros
É importante distinguir entre valores de stop_reason e erros reais:
Motivos de parada (respostas bem-sucedidas)
- Fazem parte do corpo da resposta
- Indicam por que a geração parou normalmente
- A resposta contém conteúdo válido
Erros (requisições com falha)
- Códigos de status HTTP 4xx ou 5xx
- Indicam falhas no processamento da requisição
- A resposta contém detalhes do erro
client = anthropic.Anthropic()
try:
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
# Trata a resposta bem-sucedida com stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIStatusError as e:
# Trata os erros reais
match e.status_code:
case 429:
print("Rate limit exceeded")
case 500:
print("Server error")Considerações sobre streaming
Ao usar streaming, o stop_reason é:
nullno evento inicialmessage_start- Fornecido no evento
message_delta - Não fornecido em nenhum outro evento
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}")Padrões comuns
Lidando com fluxos de trabalho de uso de ferramentas
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":
# Executa as ferramentas e continua
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Resposta final
return responseGarantindo respostas completas
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
# Continua de onde parou
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Please continue from where you left off."},
]
return full_responseObtendo o máximo de tokens sem saber o tamanho da entrada
Com o motivo de parada model_context_window_exceeded, você pode solicitar o máximo possível de tokens sem calcular o tamanho da entrada:
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":
# Obteve o máximo de tokens possível dado o tamanho da entrada
print(
f"Generated {response.usage.output_tokens} tokens (context limit reached)"
)
case "max_tokens":
# Obteve exatamente os tokens solicitados
print(
f"Generated {response.usage.output_tokens} tokens (max_tokens reached)"
)
case _:
# Conclusão natural
print(
f"Generated {response.usage.output_tokens} tokens (natural completion)"
)
return next((block.text for block in response.content if block.type == "text"), "")Próximos passos
Tente novamente requisições recusadas em um modelo de fallback, no lado do servidor ou no seu cliente.
Deixe o SDK gerenciar o loop de tool_use, a formatação de resultados e as novas tentativas para você.
Leia o stop_reason do evento message_delta ao usar streaming.
Lide com erros HTTP 4xx e 5xx, que são distintos dos motivos de parada.
Was this page helpful?