Resposta da API da OpenAI cortada? Como detectar o limite de tokens de saída

Resposta rápida

Na Responses API, a resposta cortada pelo limite volta com status "incomplete" e incomplete_details.reason igual a "max_output_tokens". No Chat Completions, o sinal é finish_reason igual a "length". Em modelos de raciocínio, o limite soma o raciocínio e o texto visível, então um valor baixo pode devolver saída vazia. Para corrigir, aumente o limite, peça continuação em nova requisição ou divida a tarefa.

A requisição passou. Não veio 429, não veio 529, o servidor respondeu normalmente, mas o texto termina no meio de uma frase ou de um JSON. Isso não é erro HTTP: o modelo parou porque bateu no limite de tokens de saída, e a API registra isso num campo da própria resposta.

Cada API usa um campo diferente para esse aviso, e é aí que muito código escorrega. Na OpenAI é status: "incomplete" (Responses API) ou finish_reason: "length" (Chat Completions), e na Anthropic é stop_reason: "max_tokens". Aqui o assunto é o seu código, não a interface do chat.

Como detectar a resposta cortada na Responses API

Na Responses API, a resposta cortada volta com status: "incomplete" e com incomplete_details.reason igual a "max_output_tokens". Segundo o guia Reasoning models da OpenAI, isso acontece quando os tokens gerados atingem o valor de max_output_tokens definido na requisição ou o limite da janela de contexto.

O sintoma é o que você já viu: texto que acaba do nada. A causa é o orçamento de saída que esgotou antes de o modelo terminar.

Se liga num detalhe: reason não tem um valor só. A referência da Responses API lista dois motivos possíveis para a resposta incompleta: max_output_tokens e content_filter.

São problemas diferentes. max_output_tokens é falta de espaço, e aumentar o limite pode resolver. content_filter é bloqueio de conteúdo, e aumentar o limite não muda nada, então trate esse caso separado (logar, avisar o usuário, revisar o prompt).

O guia de reasoning mostra o padrão de checagem. Abaixo vai um exemplo didático em Python baseado nele (não é saída de teste, é a estrutura da verificação):

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="SEU_MODELO",
    input="Escreva um resumo detalhado sobre filas de mensagens.",
    max_output_tokens=2000,
)

print("status:", response.status)
if response.incomplete_details:
    print("reason:", response.incomplete_details.reason)

if response.status == "incomplete" and response.incomplete_details.reason == "max_output_tokens":
    if response.output_text:
        print("Saída parcial:", response.output_text)
    else:
        print("Limite atingido sem texto visível")
elif response.status == "incomplete" and response.incomplete_details.reason == "content_filter":
    print("Resposta interrompida por filtro de conteúdo")
else:
    print(response.output_text)

Para verificar, rode com um max_output_tokens pequeno e um pedido longo, e olhe o que é logado em status e reason. Se aparecer incomplete com max_output_tokens, sua detecção está funcionando.

Erro comum: confiar só em output_text não vazio. Uma resposta cortada pode trazer texto parcial, e o código segue achando que está tudo completo. Quem decide se acabou é o status, não o tamanho do texto.

finish_reason length: como identificar o corte no Chat Completions

No Chat Completions, o mesmo sintoma aparece em outro lugar: o campo finish_reason de cada choice. Pela referência do Chat Completions, os valores possíveis são:

  • stop: o modelo terminou naturalmente ou bateu numa stop sequence
  • length: o limite máximo de tokens foi atingido
  • tool_calls: o modelo chamou uma ferramenta
  • content_filter: o conteúdo foi barrado pelo filtro
  • function_call: descontinuado

Ou seja, finish_reason igual a length é a resposta cortada por limite de tokens.

O limite também mudou de nome. O parâmetro atual é max_completion_tokens, o teto de tokens gerados numa completion, já incluindo os tokens de raciocínio. O max_tokens é o parâmetro antigo, marcado como descontinuado e incompatível com modelos de raciocínio da série o, segundo a mesma referência.

Exemplo didático da checagem:

from openai import OpenAI

client = OpenAI()

completion = client.chat.completions.create(
    model="SEU_MODELO",
    messages=[{"role": "user", "content": "Liste 30 ideias de projetos com explicação."}],
    max_completion_tokens=500,
)

choice = completion.choices[0]
print("finish_reason:", choice.finish_reason)

if choice.finish_reason == "length":
    print("Resposta cortada no limite de tokens")
elif choice.finish_reason == "content_filter":
    print("Resposta barrada por filtro de conteúdo")
else:
    print(choice.message.content)

Erro comum: código antigo que ainda passa max_tokens e funcionava com o modelo anterior. Na hora de trocar para um modelo de raciocínio, esse parâmetro deixa de servir. Faça a troca para max_completion_tokens junto com a troca de modelo.

Por que modelos de raciocínio devolvem resposta vazia com max_output_tokens baixo

Porque, nesses modelos, max_output_tokens limita o total gerado: tokens de raciocínio mais a saída visível. O raciocínio não aparece no texto, mas consome o mesmo orçamento.

Se o limite é baixo, o modelo pode gastar tudo pensando. Aí a resposta volta incomplete com reason max_output_tokens, sem nenhum item de mensagem no output (o output_text fica vazio), e os tokens de raciocínio são cobrados mesmo assim. Tudo isso está descrito no guia Reasoning models.

É aquele caso clássico de "a API respondeu, mas não veio nada". E você ainda paga pelo raciocínio…

Como verificar? Olhe o objeto usage. A quantidade de tokens de raciocínio fica em usage.output_tokens_details.reasoning_tokens, e output_tokens menos reasoning_tokens dá quantos tokens foram para o texto visível.

usage = response.usage
raciocinio = usage.output_tokens_details.reasoning_tokens
visivel = usage.output_tokens - raciocinio
print(f"raciocínio: {raciocinio} | saída visível: {visivel}")

Se a saída visível estiver em zero ou perto disso, o orçamento morreu no raciocínio.

Para prevenir, o mesmo guia recomenda reservar pelo menos 25.000 tokens para raciocínio e saída quando você começa a testar modelos de raciocínio, ajustando essa margem depois conforme o uso real.

O que fazer quando a resposta vem cortada: aumentar o limite, continuar ou dividir

Detectou o corte? Você tem três caminhos, e cada um serve para um cenário.

  1. Aumentar o limite. Suba max_output_tokens (Responses) ou max_completion_tokens (Chat Completions). Faz sentido quando a tarefa realmente precisa de uma resposta longa ou quando o usage mostra que o raciocínio comeu quase todo o orçamento.
  2. Pedir continuação. Faça uma nova requisição levando a saída parcial no contexto e pedindo para o modelo continuar de onde parou. É uma estratégia genérica, não um recurso específico da API, então valide se o trecho emendado ficou coerente (JSON cortado, por exemplo, precisa ser reconstruído inteiro).
  3. Dividir a tarefa. Em vez de "gere a documentação completa", peça seção por seção. Costuma ser o caminho mais previsível quando a saída é grande demais para caber num pedido só.

A Central de Ajuda da OpenAI lista as alavancas para controlar o tamanho da resposta: limites de tokens, configurações de raciocínio e verbosity, instruções claras, exemplos e stop sequences.

Tome cuidado com uma expectativa comum: não existe configuração de mínimo de tokens. O limite é só teto. Se você quer uma resposta mais longa, isso vai no prompt.

E a prevenção que vale para os três casos: cheque o campo de parada em toda resposta antes de usar o texto. Salvar no banco, devolver ao usuário ou parsear JSON sem essa checagem é pedir para encontrar resposta pela metade em produção.

stop_reason max_tokens: o equivalente na API da Anthropic

Na Messages API da Anthropic, o campo é stop_reason, e o valor que indica corte é max_tokens. Pela documentação Handling stop reasons, stop_reason vem em toda resposta bem-sucedida e explica por que o modelo parou, sem representar falha da requisição.

Mesmo raciocínio da OpenAI: requisição aceita, texto incompleto, sinal num campo da resposta.

Uma diferença prática: na Anthropic, max_tokens é obrigatório na requisição, segundo a referência da Messages API. Você sempre define o teto, então sempre precisa tratar o caso de bater nele.

Exemplo didático da checagem:

if message.stop_reason == "max_tokens":
    print("Resposta cortada no max_tokens da requisição")

O tratamento sugerido na documentação de stop reasons é avisar o usuário ou continuar com novas requisições ou com um max_tokens maior até a resposta terminar.

Tem um caso específico que pega muita gente: se o corte acontece no meio de um bloco de tool use, a mesma página orienta refazer a requisição com max_tokens maior para receber o tool use completo. Tentar executar uma ferramenta com argumentos pela metade é receita para bug estranho.

Próximo passo: valide o campo de parada antes de usar o texto

Abra o trecho do seu código onde a resposta da API é lida e coloque ali, antes de qualquer uso do texto, a checagem do campo de parada da API que você usa: status e incomplete_details na Responses API, finish_reason no Chat Completions ou stop_reason na Anthropic.

No mesmo ponto, registre reasoning_tokens do usage nos logs. Com alguns dias de dados você enxerga se o limite está apertado e ajusta com base no uso real, não no chute.

Fontes consultadas

Consulta em .

Veja mais exemplos práticos no canal Hora de Codar. Se quiser aprofundar o contexto, veja Huashu Design: A Skill GRÁTIS Que SUBSTITUI o Claude Design (Sem Limite).

Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 30min

Blog | Mais populares