Claude Platform Docs
MessagesConstruindo com Claude

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

ValorQuando ocorreO que fazer
end_turnClaude terminou sua resposta naturalmente.Use a resposta.
max_tokensA resposta atingiu seu limite de max_tokens.Aumente max_tokens ou continue a resposta.
stop_sequenceClaude emitiu uma das suas stop_sequences.Leia stop_sequence para ver qual foi acionada.
tool_useClaude 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_turnUm loop de ferramenta de servidor atingiu seu limite de iterações.Envie o conteúdo do assistente de volta para continuar.
refusalClaude se recusou a responder.Leia stop_details e tente novamente em um modelo de fallback.
model_context_window_exceededA 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.

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
  }
}

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",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
    # Processar a resposta completa
    for block in response.content:
        if block.type == "text":
            print(block.text)

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",
    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 continuar

stop_sequence

Claude encontrou uma das suas sequências de parada personalizadas.

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}")

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",
    max_tokens=1024,
    tools=[weather_tool],
    messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
)

if response.stop_reason == "tool_use":
    # Extrair e executar a ferramenta
    for block in response.content:
        if block.type == "tool_use":
            result = execute_tool(block.name, block.input)
            # Retornar o resultado ao Claude para a resposta final

Uma 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.

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" }
    }
  ]
}

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.

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"
    }
  ]
}

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` block

Omitir 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",
    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",
        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",
    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ção

Em 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 ou Claude Opus 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. Se você construir a nova tentativa por conta própria a partir do Claude Fable 5.1, Claude Fable 5 ou Claude Opus 5, o crédito de fallback explica como evitar pagar o custo do cache de prompt duas vezes.

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",
    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 contexto

Melhores 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):
    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:
        # 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 text

Implemente 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", 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 os papéis alternados
        messages = [
            {"role": "user", "content": user_query},
            {"role": "assistant", "content": response.content},
        ]

    # Máximo de continuações atingido - retorna a última resposta
    return response

Motivos 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",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello!"}],
    )

    # Tratar resposta bem-sucedida com stop_reason
    if response.stop_reason == "max_tokens":
        print("Response was truncated")

except anthropic.APIStatusError as e:
    # Tratar erros reais
    if e.status_code == 429:
        print("Rate limit exceeded")
    elif e.status_code == 500:
        print("Server error")

Considerações sobre streaming

Ao usar streaming, o stop_reason é:

  • null no evento inicial message_start
  • Fornecido no evento message_delta
  • Não fornecido em nenhum outro evento
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}")

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", max_tokens=1024, messages=messages, tools=tools
        )

        if response.stop_reason == "tool_use":
            # Executar ferramentas e continuar
            tool_results = execute_tools(response.content)
            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})
        else:
            # Resposta final
            return response

Garantindo 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", 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

        # Continuar 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_response

Obtendo 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",
        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":
        # Obteve o máximo possível de tokens dado o tamanho da entrada
        print(
            f"Generated {response.usage.output_tokens} tokens (context limit reached)"
        )
    elif response.stop_reason == "max_tokens":
        # Obteve exatamente os tokens solicitados
        print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
    else:
        # 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?