Quando você faz uma solicitação para a API Messages, a resposta do Claude inclui um campo stop_reason que indica por que o modelo parou de gerar sua resposta. Compreender esses valores é crucial para construir aplicações robustas que lidam adequadamente com diferentes tipos de resposta. Para detalhes sobre stop_reason na resposta da API, consulte a referência da API Messages.

O que é stop_reason?

O campo stop_reason faz parte de toda resposta bem-sucedida da API Messages. Ao contrário dos erros, que indicam falhas no processamento da sua solicitação, stop_reason informa por que o Claude completou com sucesso a geração de sua resposta.
Example response
{
  "id": "msg_01234",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Aqui está a resposta para sua pergunta..."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

Valores de razão de parada

end_turn

A razão de parada mais comum. Indica que o Claude terminou sua resposta naturalmente.
if response.stop_reason == "end_turn":
    # Processar a resposta completa
    print(response.content[0].text)

Respostas vazias com end_turn

Às vezes o Claude retorna uma resposta vazia (exatamente 2-3 tokens sem conteúdo) com stop_reason: "end_turn". Isso normalmente acontece quando o 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 (o Claude aprende a esperar que o usuário sempre insira texto após resultados de ferramentas, então ele termina seu turno para seguir o padrão)
  • Enviar a resposta completa do Claude de volta sem adicionar nada (o Claude já decidiu que terminou, então permanecerá terminado)
Como prevenir respostas vazias:
# INCORRETO: Adicionar texto imediatamente após tool_result
messages = [
    {"role": "user", "content": "Calcule a soma de 1234 e 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": "Aqui está o resultado"  # Não adicione texto após tool_result
        }
    ]}
]

# CORRETO: Enviar resultados de ferramentas diretamente sem texto adicional
messages = [
    {"role": "user", "content": "Calcule a soma de 1234 e 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"
        }
    ]}  # Apenas o tool_result, sem texto adicional
]

# Se você ainda receber respostas vazias após corrigir o acima:
def handle_empty_response(client, messages):
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=messages
    )

    # Verificar se a resposta está vazia
    if (response.stop_reason == "end_turn" and
        not response.content):

        # INCORRETO: Não apenas tente novamente com a resposta vazia
        # Isso não funcionará porque o Claude já decidiu que terminou

        # CORRETO: Adicionar um prompt de continuação em uma NOVA mensagem do usuário
        messages.append({"role": "user", "content": "Por favor, continue"})

        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1024,
            messages=messages
        )

    return response
Melhores práticas:
  1. Nunca adicione blocos de texto imediatamente após resultados de ferramentas - Isso ensina o Claude a esperar entrada do usuário após cada uso de ferramenta
  2. Não tente novamente respostas vazias sem modificação - Simplesmente enviar a resposta vazia de volta não ajudará
  3. Use prompts de continuação como último recurso - Apenas se as correções acima não resolverem o problema

max_tokens

O Claude parou porque atingiu o limite max_tokens especificado em sua solicitação.
# Solicitação com tokens limitados
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=10,
    messages=[{"role": "user", "content": "Explique física quântica"}]
)

if response.stop_reason == "max_tokens":
    # Resposta foi truncada
    print("Resposta foi cortada no limite de tokens")
    # Considere fazer outra solicitação para continuar

stop_sequence

O Claude encontrou uma de suas sequências de parada personalizadas.
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    stop_sequences=["FIM", "PARAR"],
    messages=[{"role": "user", "content": "Gere texto até você dizer FIM"}]
)

if response.stop_reason == "stop_sequence":
    print(f"Parou na sequência: {response.stop_sequence}")

tool_use

O Claude está chamando uma ferramenta e espera que você a execute.
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=[weather_tool],
    messages=[{"role": "user", "content": "Qual é o clima?"}]
)

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

pause_turn

Usado com ferramentas de servidor como busca na web quando o Claude precisa pausar uma operação de longa duração.
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=[{"type": "web_search_20250305", "name": "web_search"}],
    messages=[{"role": "user", "content": "Busque as últimas notícias de IA"}]
)

if response.stop_reason == "pause_turn":
    # Continuar a conversa
    messages = [
        {"role": "user", "content": original_query},
        {"role": "assistant", "content": response.content}
    ]
    continuation = client.messages.create(
        model="claude-sonnet-4-5",
        messages=messages,
        tools=[{"type": "web_search_20250305", "name": "web_search"}]
    )

refusal

O Claude se recusou a gerar uma resposta devido a preocupações de segurança.
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "[Solicitação insegura]"}]
)

if response.stop_reason == "refusal":
    # Claude se recusou a responder
    print("Claude não conseguiu processar esta solicitação")
    # Considere reformular ou modificar a solicitação
Se você encontrar razões de parada refusal frequentemente ao usar Claude Sonnet 4.5 ou Opus 4.1, você pode tentar atualizar suas chamadas de API para usar Sonnet 4 (claude-sonnet-4-20250514), que tem diferentes restrições de uso. Saiba mais sobre entendendo os filtros de segurança da API do Sonnet 4.5.
Para saber mais sobre recusas acionadas por filtros de segurança da API para Claude Sonnet 4.5, consulte Entendendo os Filtros de Segurança da API do Sonnet 4.5.

model_context_window_exceeded

O Claude parou porque atingiu o limite da janela de contexto do modelo. Isso permite que você solicite o máximo de tokens possível sem conhecer o tamanho exato da entrada.
# Solicitação com tokens máximos para obter o máximo possível
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=64000,  # Tokens de saída máximos do modelo
    messages=[{"role": "user", "content": "Entrada grande que usa a maior parte da janela de contexto..."}]
)

if response.stop_reason == "model_context_window_exceeded":
    # Resposta atingiu limite da janela de contexto antes de max_tokens
    print("Resposta atingiu limite da janela de contexto do modelo")
    # A resposta ainda é válida mas foi limitada pela janela de contexto
Esta razão de parada está disponível por padrão no Sonnet 4.5 e modelos mais recentes. Para modelos anteriores, use o cabeçalho beta model-context-window-exceeded-2025-08-26 para habilitar este comportamento.

Melhores práticas para lidar com razões de parada

1. Sempre verifique stop_reason

Torne um hábito verificar o stop_reason em sua lógica de tratamento de resposta:
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:
        # Lidar com end_turn e outros casos
        return response.content[0].text

2. Lidar com respostas truncadas graciosamente

Quando uma resposta é truncada devido a limites de tokens ou janela de contexto:
def handle_truncated_response(response):
    if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
        # Opção 1: Avisar o usuário sobre o limite específico
        if response.stop_reason == "max_tokens":
            message = "[Resposta truncada devido ao limite max_tokens]"
        else:
            message = "[Resposta truncada devido ao limite da janela de contexto]"
        return f"{response.content[0].text}\n\n{message}"

        # Opção 2: Continuar geração
        messages = [
            {"role": "user", "content": original_prompt},
            {"role": "assistant", "content": response.content[0].text}
        ]
        continuation = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=1024,
            messages=messages + [{"role": "user", "content": "Por favor, continue"}]
        )
        return response.content[0].text + continuation.content[0].text

3. Implementar lógica de retry para pause_turn

Para ferramentas de servidor que podem pausar:
def handle_paused_conversation(initial_response, max_retries=3):
    response = initial_response
    messages = [{"role": "user", "content": original_query}]
    
    for attempt in range(max_retries):
        if response.stop_reason != "pause_turn":
            break
            
        messages.append({"role": "assistant", "content": response.content})
        response = client.messages.create(
            model="claude-sonnet-4-5",
            messages=messages,
            tools=original_tools
        )
    
    return response

Razões de parada vs. erros

É importante distinguir entre valores de stop_reason e erros reais:

Razões de parada (respostas bem-sucedidas)

  • Parte do corpo da resposta
  • Indicam por que a geração parou normalmente
  • Resposta contém conteúdo válido

Erros (solicitações falhadas)

  • Códigos de status HTTP 4xx ou 5xx
  • Indicam falhas no processamento da solicitação
  • Resposta contém detalhes do erro
try:
    response = client.messages.create(...)
    
    # Lidar com resposta bem-sucedida com stop_reason
    if response.stop_reason == "max_tokens":
        print("Resposta foi truncada")
    
except anthropic.APIError as e:
    # Lidar com erros reais
    if e.status_code == 429:
        print("Limite de taxa excedido")
    elif e.status_code == 500:
        print("Erro do servidor")

Considerações de streaming

Ao usar streaming, stop_reason é:
  • null no evento inicial message_start
  • Fornecido no evento message_delta
  • Não fornecido em nenhum outro evento
with client.messages.stream(...) as stream:
    for event in stream:
        if event.type == "message_delta":
            stop_reason = event.delta.stop_reason
            if stop_reason:
                print(f"Stream terminou com: {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-sonnet-4-5",
            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-sonnet-4-5",
            messages=messages,
            max_tokens=4096
        )

        full_response += response.content[0].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": "Por favor, continue de onde você parou."}
        ]

    return full_response

Obtendo tokens máximos sem conhecer o tamanho da entrada

Com a razão de parada model_context_window_exceeded, você pode solicitar o máximo de tokens possível sem calcular o tamanho da entrada:
def get_max_possible_tokens(client, prompt):
    """
    Obter o máximo de tokens possível dentro da janela de contexto do modelo
    sem precisar calcular a contagem de tokens de entrada
    """
    response = client.messages.create(
        model="claude-sonnet-4-5",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=64000  # Definir para tokens de saída máximos do modelo
    )

    if response.stop_reason == "model_context_window_exceeded":
        # Obteve o máximo de tokens possível dado o tamanho da entrada
        print(f"Gerou {response.usage.output_tokens} tokens (limite de contexto atingido)")
    elif response.stop_reason == "max_tokens":
        # Obteve exatamente os tokens solicitados
        print(f"Gerou {response.usage.output_tokens} tokens (max_tokens atingido)")
    else:
        # Conclusão natural
        print(f"Gerou {response.usage.output_tokens} tokens (conclusão natural)")

    return response.content[0].text
Ao lidar adequadamente com valores de stop_reason, você pode construir aplicações mais robustas que lidam graciosamente com diferentes cenários de resposta e fornecem melhores experiências do usuário.