TL;DR — In LangChain, the DeepSeek reasoner 400 shows up when your chain's message history replays assistant turns without the
reasoning_contentDeepSeek attached to them — most commonly in agent loops with tool calling, custom memory classes, or message-trimming middleware. Fixes: keeplangchain-deepseekcurrent (round-trip handling has improved across 2026 releases), make sure your memory preservesadditional_kwargson AI messages instead of reconstructing bare messages, or route through an endpoint that restores the field server-side.
Why LangChain setups hit this
LangChain represents turns as message objects, and DeepSeek's thinking trace rides along in the AI message's additional fields. Three common patterns break the round-trip:
- Custom memory / history classes that rebuild messages from plain text (
AIMessage(content=...)) — the thinking block never makes it back - Message trimming or summarization middleware that rewrites history before the next call
- Older integration versions predating full reasoning-field support — the 2026 releases of the DeepSeek integration handle the round-trip much better, so pinning an old version is a self-inflicted 400
The checklist
- Upgrade first. Current
langchain-deepseekreleases handlereasoning_contentin tool-calling flows; changelogs across 2026 specifically address reasoner + tools. If you are pinned months back, upgrade before touching code. - Preserve the original message objects. Append the exact
AIMessagethe model returned (with itsadditional_kwargs) to history — do not reconstruct messages from strings. - Audit anything that touches history. Trimmers, summarizers, and serializers must pass unknown fields through rather than dropping them.
- Test with a two-step tool chain. The bug only fires on the second request of a tool loop — a single-shot test will never catch it.
The infrastructure alternative
If the chain logic is not yours to change (or you support multiple reasoning providers), move the problem out of the framework: point LangChain's OpenAI-compatible client at CodeRouter and let the router own provider-specific field contracts. Your chain keeps bare messages; the router restores what each provider requires — the same class of fix as for proxies and degraded-reasoning warnings.