Erro 400 com reasoning_content no DeepSeek: como montar o histórico da conversa sem quebrar a próxima chamada

Fluxo mostrando o campo reasoning_content DeepSeek sendo reenviado no histórico da conversa com o parâmetro tools
Resposta rápida

Se a sua integração quebrou no segundo turno, a regra do reasoning_content DeepSeek mudou e depende de um fator só: a presença do parâmetro tools na requisição. Sem tools, o campo pode ser reenviado à vontade, porque a API ignora ele e não concatena no contexto. Com tools, o reenvio é obrigatório em todas as chamadas seguintes, inclusive nos turnos em que o modelo não fez tool call, e omitir devolve HTTP 400. Orientações antigas associadas ao deepseek-reasoner recomendavam remover o campo, e esse entendimento está desatualizado 🙂

Fala aí, beleza? O primeiro turno passa liso, você manda a segunda mensagem da mesma conversa e a API devolve 400 do nada

No modo de raciocínio da DeepSeek, a cadeia de pensamento não vem embutida na resposta: ela volta em um campo separado chamado reasoning_content, irmão de content dentro da mensagem

A cada turno o modelo devolve as duas coisas: o CoT em reasoning_content e a resposta final em content

E é exatamente o que você faz com esse campo na hora de remontar o histórico que decide se a próxima chamada passa ou quebra

O detalhe cruel é que a orientação parece ter invertido. Guias antigos associados ao deepseek-reasoner recomendavam remover o campo antes da próxima chamada. Hoje, em cenário com tools, é o contrário: não mandar é que dá 400

Erro 400 ao continuar a conversa com tools: sintoma, causa e correção

O sintoma:

A primeira requisição funciona, o modelo responde (às vezes até faz um tool call), e a segunda requisição da MESMA conversa morre com HTTP 400

Integradores relatam a mensagem The reasoning_content in the thinking mode must be passed back to the API

Se você só olha o content no log, parece que está tudo certo no array de mensagens, e aí vem o desespero de reler o payload dez vezes

Formação Claude Code
Formação Recomendada

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 118 aulas
  • 4 projetos
  • 9h 33min

A causa:

Clientes compatíveis com OpenAI costumam descartar silenciosamente campos que eles não conhecem quando remontam o histórico

E reasoning_content é justamente um campo que não existe no schema padrão deles

Resultado: o campo some no caminho, sem aviso, sem log, sem exceção. A sua mensagem de assistant volta pra API só com content, e a API recusa

Porque a documentação do modo de raciocínio é clara na separação:

  • Sem tools na requisição: o reasoning_content não precisa ser reenviado. Se for enviado, é ignorado e não entra no contexto (não gera erro)
  • Com tools na requisição: o reasoning_content PRECISA ser reenviado integralmente em todas as requisições seguintes, inclusive nos turnos em que o modelo não fez tool call. Não reenviar retorna 400

Ou seja: o mesmo código que roda lindo no seu chat simples explode no seu agente 😛

A solução:

Preservar o campo irmão de content na mensagem do assistant e devolver ele integral, sem cortar, sem resumir, sem "limpar pra economizar token"

A mensagem que volta pro array precisa ficar mais ou menos assim:

{
  "role": "assistant",
  "content": "a resposta final que o usuário vê",
  "reasoning_content": "a cadeia de pensamento que veio junto nesse mesmo turno",
  "tool_calls": [ ... ]
}

Integral mesmo, beleza? A regra vale pra TODAS as requisições subsequentes daquela conversa, não só pra próxima

Como prevenir:

Mapeie o campo explicitamente no seu cliente

Se você usa um wrapper compatível com OpenAI, não confie no round-trip automático do objeto: leia reasoning_content da resposta e escreva ele de volta na mão, no seu próprio modelo de mensagem

E use a página de exemplo oficial de tool call em modo de raciocínio como referência do formato do array de mensagens, em vez de adivinhar

Tome cuidado com serialização também: se a sua camada de persistência salva só role e content no banco, o campo morre ali e o 400 vai voltar depois do primeiro reload da sessão

Erro 400 com tool_choice: o que o modo de raciocínio não aceita

Tem um segundo 400 que aparece mesmo com o histórico impecável, e a causa dele é OUTRA

O sintoma:

Você corrigiu o histórico, o reasoning_content está indo redondinho, e a requisição continua voltando 400

A causa:

No modo de raciocínio não existe suporte a tool_choice do tipo required nem a escolha de ferramenta nomeada

Nesses dois casos a API retorna 400, independente do teu array de mensagens estar perfeito

Faz sentido quando você pensa no PORQUÊ: forçar a mão do modelo ("você VAI chamar essa função agora") briga com o modelo decidindo o caminho durante o raciocínio

A solução:

Pra usar required ou ferramenta nomeada, é preciso desligar o modo de raciocínio naquela chamada

Não dá pra ter os dois ao mesmo tempo, e a doc do modo de raciocínio é a referência pra conferir a sintaxe exata do parâmetro que faz esse desligamento

Como prevenir:

Checar a configuração de tool_choice ANTES de habilitar raciocínio

E se liga nisso: o modo de raciocínio vem ativo por padrão, com reasoning_effort padrão em high

Ou seja, você pode nem ter "ligado" nada, e ainda assim está no thinking mode com esforço alto. Muita gente se ferra aqui achando que está numa chamada comum

A chamada parou de responder: nomes de modelo legados aposentados

Tem ainda um caso em que o erro não tem nada a ver com o teu payload

O sintoma:

Requisições que funcionavam há semanas, sem nenhum deploy seu no meio, passam a retornar erro

A causa:

Os nomes de modelo legados deepseek-chat e deepseek-reasoner foram aposentados e não são mais acessíveis

A aposentadoria está registrada na página de updates da API para 24/07/2026, às 15:59 UTC, e requisições com os nomes antigos passam a retornar erro

A solução:

Cuidado aqui, porque a troca não é tão simples quanto parece: deepseek-v4-flash também já foi aposentado e está apenas temporariamente roteado para o V4.1 Flash por compatibilidade. O nome de modelo atual e correto é deepseek-flash

E se o teu código usa deepseek-v4-pro, fica o alerta: desde 14/09/2026 (04:00 UTC), toda requisição com esse nome está sendo roteada para o V4.1-Flash, com as tarifas do V4.1-Flash, até o lançamento do V4.1-Pro. Ou seja, o nome ainda responde e não quebra com 400, mas não aponta mais pro modelo Pro nem pras tarifas Pro que você imagina estar pagando

{
  "model": "deepseek-flash",
  "messages": [ ... ],
  "tools": [ ... ]
}

Como prevenir:

Fixe o nome do modelo em UM lugar de configuração, variável de ambiente ou arquivo de config, e nunca espalhado como string solta em cinco arquivos diferentes

Quando o fornecedor aposenta ou reroteia um nome, a troca vira uma linha em vez de uma caçada. E como esse tipo de mudança pode acontecer de novo, vale acompanhar a página de updates de vez em quando em vez de confiar que o nome de hoje vai valer pra sempre

E olha: se o teu contato com o modelo é só colar a chave em uma ferramenta de terceiro, tipo conectar o DeepSeek no Janitor AI, essa dor específica de array de mensagens nem te alcança, porque quem monta o histórico é a plataforma

Parâmetros que não fazem o que você espera no modo de raciocínio

Agora a parte traiçoeira: as armadilhas que NÃO dão erro

O 400 pelo menos te avisa. Aqui a requisição passa, retorna 200, e simplesmente ignora o que você pediu 😀

Parâmetro Comportamento no modo de raciocínio
temperature Aceito sem erro (compatibilidade), porém sem efeito nenhum
presence_penalty Aceito sem erro, porém sem efeito nenhum
frequency_penalty Aceito sem erro, porém sem efeito nenhum
top_p Funciona, mas com piso: valores abaixo de 0.95 são elevados para 0.95

Então aquele temperature: 0.1 que você colocou pra "deixar mais determinístico" não está fazendo NADA

E o top_p: 0.3 que você ajustou com carinho virou 0.95 na prática

Se o teu resultado veio mais criativo do que você esperava, o problema não é o prompt, é o parâmetro que nunca chegou a valer

E o max_tokens?

O valor aceito pela API vai de 1 até 384K (393216)

Os defaults mudam conforme o modo, e é isso que costuma pegar todo mundo de surpresa na conta de token:

  • 8K no modo não pensante
  • 64K no modo de raciocínio
  • 128K com reasoning_effort em max

Os níveis de esforço de raciocínio suportados pelo V4-Pro e pelo V4-Flash são três: low, high e max

Repare no salto: sem definir nada, você sai de 8K pra 64K só por estar no thinking mode, que é o padrão

E respostas mais longas demoram mais, o que aumenta a chance de estourar o teu tempo de espera. Se o sintoma for esse, o caminho é outro: retry com espera crescente e fallback resolve melhor do que mexer em max_tokens no chute

Teste em duas rodadas: como observar a diferença de resposta e de tokens

Bora ver na prática? O jeito de não ficar na fé é reproduzir os DOIS cenários na sua própria aplicação, um com tools e outro sem

Em cada chamada, olhe o objeto usage da resposta: dentro do detalhamento de tokens de completion existe o campo reasoning_tokens, e é ele que te mostra quanto foi gasto de raciocínio naquele turno

  1. Rodada 1, primeira chamada, SEM tools. Mande uma pergunta qualquer que exija raciocínio e guarde a resposta INTEIRA do assistant, content e reasoning_content juntos. Erro comum deste passo: logar só o content e descobrir depois que você não tem mais o CoT pra reenviar no teste
  1. Segunda chamada da rodada 1, reenviando o reasoning_content. Continue a conversa com o campo presente no histórico. Ela passa: sem tools, o campo é ignorado e não é concatenado ao contexto. Erro comum deste passo: concluir daqui que "pode mandar sempre" ou que "nunca dá problema"
  1. Repita a mesma segunda chamada, agora removendo o reasoning_content. Compare a resposta final com a do passo anterior. Sem tools, os dois caminhos são aceitos
  1. Anote o reasoning_tokens das três chamadas. É a sua régua de custo, e é o número que muda quando você mexe no reasoning_effort entre low, high e max
  1. Rodada 2, agora COM tools na requisição. Refaça a mesma conversa, dessa vez com o parâmetro tools presente, e reenvie o reasoning_content integral em todas as chamadas seguintes, inclusive nos turnos em que o modelo NÃO fez tool call. Erro comum deste passo: reenviar só quando teve tool call, achando que nos turnos "normais" não precisa
  1. Quebre de propósito. Na requisição seguinte, omita o reasoning_content da mensagem de assistant e confirme o HTTP 400. Sim, quebrar de propósito é útil: agora você sabe reconhecer esse 400 em produção em dez segundos, em vez de dez horas
  1. Compare as duas rodadas lado a lado. Mesmo histórico, mesma pergunta, um único parâmetro de diferença. Erro comum deste passo, e o mais caro de todos: assumir que o comportamento observado sem tools vale pro cenário com tools. É justamente onde a regra inverte

Se você mantém testes automatizados, esse passo 6 vira um teste de regressão ótimo: qualquer refatoração que "limpe" o array de mensagens quebra ele na hora

Conteúdo complementar do canal:

Pra quem quer ver IA lendo código de verdade e produzindo artefato em cima disso, este vídeo do canal mostra a skill Archify:

Conclusão

No fim, a regra do reasoning_content gira em torno de UM fator só: a requisição carrega tools ou não?

Sem tools, o campo é ignorado e não entra no contexto, então mandar ou não mandar dá no mesmo

Com tools, o reenvio integral é obrigatório em todas as chamadas seguintes, e esquecer disso é HTTP 400 na cara

O que confunde é que orientações antigas associadas ao deepseek-reasoner recomendavam remover o campo antes da próxima chamada, o oposto do que vale hoje pra cenários com tools. Aquele modelo já foi aposentado, junto com deepseek-chat, então tratar guias antigos como verdade atual é receita de bug

Próximo passo: roda o teste das duas rodadas na tua aplicação, compara o reasoning_tokens e confere o exemplo oficial de tool call em modo de raciocínio antes de escrever a tua camada de histórico

Até o próximo post! =)

Perguntas frequentes

Preciso reenviar o reasoning_content se a minha conversa não usa tools?

Não. Sem o parâmetro tools na requisição, o reasoning_content não precisa voltar no histórico. Se você mandar mesmo assim, ele é ignorado e não entra no contexto, sem gerar erro.

Por que a mesma conversa funciona no turno 1 e quebra 400 no turno 2?

Porque no primeiro turno não existe histórico anterior pra remontar, então o problema não aparece ainda. No segundo turno, se a requisição carrega tools e o reasoning_content do assistant sumiu do array, a API recusa com 400.

O reasoning_content conta como tokens de raciocínio cobrados?

O detalhamento de uso da resposta traz o campo reasoning_tokens dentro do objeto usage, mostrando quantos tokens foram gerados pra cadeia de pensamento. Isso dá pra comparar o custo entre uma chamada com e sem reenvio do campo.

Dá pra usar tool_choice required com o thinking mode ligado?

Não. No modo de raciocínio não há suporte a tool_choice required nem à escolha de ferramenta nomeada, e a API retorna 400 nesses casos. É preciso desligar o modo de raciocínio pra usar esse tipo de tool_choice.

Definir temperature no modo de raciocínio dá erro?

Não dá erro. temperature, presence_penalty e frequency_penalty são aceitos por compatibilidade no modo de raciocínio, mas não têm efeito nenhum no resultado.

Minhas requisições com deepseek-chat pararam de funcionar do nada, o que houve?

Os nomes legados deepseek-chat e deepseek-reasoner foram aposentados e não são mais acessíveis. Mas atenção: deepseek-v4-flash também já foi aposentado e está só temporariamente roteado, então o nome atual e correto é deepseek-flash. E deepseek-v4-pro, embora ainda responda, está sendo roteado para o V4.1-Flash (com tarifas de Flash) desde 14/09/2026, até o lançamento do V4.1-Pro.




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.

Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Blog | Mais populares