Fix DeepSeek 400 Error: reasoning_content in thinking mode must be passed back
DeepSeek returns HTTP 400 on multi-turn tool calls because standard OpenAI client libraries strip the non-standard reasoning_content field when replaying message history.
When calling DeepSeek thinking-enabled models (such as deepseek-v4.1-flash-1017), the first prompt generates an answer or executes a tool without
issue. But as soon as your client sends the tool result back for the next turn, the API rejects the
request with HTTP 400:
API Error: 400 {"error":{"message":"The `reasoning_content` in the thinking mode must be passed back to the API.","type":"invalid_request_error"}}
Some gateways report the same failure with different wording: Missing reasoning_content
field in the assistant message at message index X or thinking is enabled but
reasoning_content is missing in assistant tool call message. The root cause is identical — the
reasoning text the model produced on the previous turn was dropped by the client when it assembled
the message history.
Why the first turn succeeds but tool calls immediately fail with 400
In turn 1, you send only a user prompt (or system instructions). The model enters thinking mode, generates its internal reasoning process, and then produces either final text or a tool call.
DeepSeek's API contract enforces a strict rule: once thinking mode is active, any subsequent request that replays an assistant message containing tool calls must pass the original reasoning tokens back in that assistant message. Turn 1 does not fail because there is no prior assistant message to validate. The failure triggers at turn 2, right when your client submits the conversation history: the initial user message, the assistant's tool-call response, and the tool execution result.
Standard OpenAI message format strips reasoning_content
The standard OpenAI Chat Completions specification only defines role,
content, and tool_calls on assistant messages:
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {"name": "get_current_files", "arguments": "{}"}
}
]
}
Most agent frameworks, client plugins, and proxy adapters adhere strictly to the OpenAI schema.
When storing turn history or preparing the turn 2 payload, they discard unrecognized top-level
fields. The returned reasoning_content string is quietly dropped. Other reasoning models
(such as Kimi) enforce a similar requirement and fail with equivalent errors when proxy adapters
strip reasoning blocks.
DeepSeek requires the replayed assistant message to retain the reasoning payload alongside the tool calls:
{
"role": "assistant",
"content": "",
"reasoning_content": "Planning the tool call steps...",
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {"name": "get_current_files", "arguments": "{}"}
}
]
}
On the Anthropic-compatible route the same rule applies in a different shape: DeepSeek expects a
type: "thinking" block preserved inside the content array. Whichever route
you use, if a proxy layer or client filters that content out, the next request fails with 400.
Upstream validation requires the key to be present on assistant messages in thinking mode. If the
original reasoning text was already discarded by an upstream proxy, passing an empty string
"reasoning_content": "" satisfies the schema validation on the OpenAI completions route,
whereas omitting the field entirely results in an immediate 400 rejection.
Preserving and sending reasoning_content back in code and client configs
If you are orchestrating tool calls directly with the OpenAI Python SDK or an equivalent HTTP
client, explicitly preserve reasoning_content when appending the assistant message to
your message list:
assistant_msg = response.choices[0].message
messages.append({
"role": "assistant",
"content": assistant_msg.content or "",
"reasoning_content": getattr(assistant_msg, "reasoning_content", ""),
"tool_calls": [tc.model_dump() for tc in assistant_msg.tool_calls] if assistant_msg.tool_calls else None
})
messages.append({
"role": "tool",
"tool_call_id": assistant_msg.tool_calls[0].id,
"content": "dir1/\nfile1.txt"
})
If you encounter this error in coding agents like Roo Code or Continue, check your provider configuration:
- In Roo Code, select the native DeepSeek provider profile (v3.36.10 or later) rather than a
generic OpenAI-compatible proxy profile. Native profiles preserve
reasoning_contentduring replay, while generic OpenAI proxy paths strip it. - In Continue, set
providertodeepseekinstead of the genericopenaiprovider, which drops non-standard fields when it serializes history.
Switching to DeepSeek partway through an existing session fails the same way: the earlier assistant messages were produced by another model and carry no reasoning field at all. Clear the context and start a new session.
When running tools like Claude Code, Codex, or the OpenAI SDK against upstream models, managing
custom endpoint URLs and provider headers across disparate development tools adds operational
friction. Routing through AnRouter lets you access DeepSeek and other
supported models via standardized endpoints (POST /v1/chat/completions or
POST /v1/messages), eliminating base URL sprawl while you keep client payloads
compliant with upstream model specs.