Repository navigation
[Bug]: DeepSeek V4 Flash Responses continuation still drops reasoning_text on 2.12.0 #1477
Description
Client or integration
Codex Desktop using OpenCodex as the local Responses API proxy.
Summary
On OpenCodex 2.12.0, the official DeepSeek V4 Flash Responses route can still fail after tool calls with:
The `reasoning_text` in the thinking mode must be passed back to the API.
This reproduces even though the built-in deepseek registry entry already sets preserveResponsesReasoningContent: true and the router is expected to merge that registry capability into the effective provider config.
This appears to be a regression or an uncovered reasoning-replay path related to #875 and #1193, rather than a missing user configuration flag.
Reproduction
- Start a Codex Desktop turn using
deepseek/deepseek-v4-flashwith High reasoning. - Give it a coding task that requires several shell/tool calls.
- Let the model issue tools and return their results.
- Observe the continuation request after the tool results.
The failure occurred twice in the same thread. In both attempts, several shell reads completed successfully and the next model continuation failed with the same upstream error.
Actual result
The turn ends in systemError with an upstream 400 payload:
{
"error": {
"message": "The `reasoning_text` in the thinking mode must be passed back to the API.",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request_error"
}
}Plain turns and earlier requests can succeed; the failure appears specifically on a tool-call continuation that needs prior reasoning replay.
Expected result
For DeepSeek thinking-mode continuations, OpenCodex should pass the prior plaintext reasoning item back to the upstream Responses API so the tool loop can continue.
Version
OpenCodex 2.12.0, installed through npm.
Operating system
Windows 11.
Provider and route details
- Provider: official DeepSeek API (
https://api.deepseek.com) - Provider ID:
deepseek - Model:
deepseek/deepseek-v4-flash - Reasoning effort: High
- Inbound API: Codex Responses API
- Upstream route: DeepSeek
POST /responses
Why this does not look like a local config omission
The 2.12.0 source already contains all of the following:
- the built-in DeepSeek registry entry sets
preserveResponsesReasoningContent: true; router.tsmerges that registry value when the saved provider does not explicitly override it;openai-responses.tspreserves raw reasoning content when the effective provider flag is true.
The saved provider does not explicitly set the flag to false. Therefore adding the same value manually would only duplicate the built-in default and should not be required.
Related issues
- #875 fixed reasoning sanitization and DeepSeek Responses tool-loop handling.
- [Bug] preserveReasoningContentModels still emits bare tool_call continuations when the replay cache misses (long sessions) — DeepSeek thinking mode 400 #1193 fixed placeholder handling for replay-cache misses on OpenAI Chat-compatible routes.
This reproduction is on the current 2.12.0 release and uses the native DeepSeek Responses route, so it may be a new replay/state edge case or a regression after those fixes.
I can provide sanitized request/debug rows if a narrower capture is needed.
Activity
github-actions commentedon Aug 11, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
[-][Bug] DeepSeek V4 Flash Responses continuation still drops reasoning_text on 2.12.0[/-][+][Bug]: DeepSeek V4 Flash Responses continuation still drops reasoning_text on 2.12.0[/+]c3right commentedon Aug 11, 2026
Additional controlled reproduction narrows the trigger substantially.
Environment
26.803.41515(Powered by Codex & OWL, released 2026-08-07)2.12.0v1gpt-5.6-luna, reasoning efforthighdeepseek/deepseek-v4-flashmaxresponsesopenai-responsesMinimal control matrix
Using the same DeepSeek sub-agent route:
The two-tool sequential case was verified from the child rollout: call #2 was emitted only after the
function_call_outputfor call #1 had returned.A real production task showed the same failing shape: one reasoning item was followed by two file-read tool calls emitted before the first tool result returned; after both tool outputs came back, the next continuation failed immediately with:
{ "error": { "message": "The `reasoning_text` in the thinking mode must be passed back to the API.", "type": "invalid_request_error", "param": null, "code": "invalid_request_error" } }A/B custom-agent test
Baseline agent (
deepseek_executor_max) and serial variant used the same model, samemaxeffort, and the same simple two-file read task.Baseline behavior (no parallel-tool restriction):
spawn_agent: succeedserroredThe reasoning_text in the thinking mode must be passed back to the API.Serial variant changed only the tool-use policy:
Result:
spawn_agent: succeedscompletedThis provides a deterministic workaround: one tool call per model turn for DeepSeek sub-agents.
OpenCodex log correlation from the failing baseline
The first DeepSeek request and the failing continuation have the same OpenCodex conversation ID.
First request:
deepseek-v4-flashmaxopenai-responses200reasoningOutputTokens:78Continuation request:
deepseek-v4-flashmaxopenai-responses400sendCount:1recoveryKinds:[]The reasoning_text in the thinking mode must be passed back to the API.So routing and effort propagation are working, and the upstream rejection occurs specifically on the continuation after same-turn parallel tool calls.
I did not have provider-debug request bodies enabled for this run, so this still does not prove whether the reasoning item is already absent in the inbound Codex continuation or is lost/mis-associated inside OpenCodex's Responses -> DeepSeek translation. The controlled A/B does, however, isolate same-turn parallel tool calls as the trigger and strict sequential tool use as a reliable workaround.
Ingwannu commentedon Aug 11, 2026
Confirmed against current
dev: this is a real regression in the provider-scoped normalization added for #1292.For a valid same-turn parallel history such as
the current per-pair normalizer rewrites it as
reasoning -> call A -> output A -> call B -> output B. DeepSeek documents thatreasoningandfunction_callitems are merged into the adjacent assistant message, and its Responses route always enables parallel tool calling. The rewrite therefore splits call B away from the reasoning block that belongs to the original assistant turn, which matches the controlled sequential-vs-parallel A/B in this report.I will fix this narrowly on
devby normalizing an unambiguous parallel batch as all calls followed by their matching outputs, while still moving hook-injected non-tool context after the batch. The existing single-call #1292 behavior will remain intact; tolerant providers and ambiguous/duplicate call IDs will remain unchanged. Regression coverage will include single-call interleaving, parallel calls, parallel calls with injected context, and the tolerant-provider control. I will link the PR and exact validation here; the issue should remain open until integration.Ingwannu commentedon Aug 11, 2026
Implementation is now in draft PR #1479, targeting
dev.The fix keeps all calls from one unambiguous parallel assistant turn together, followed by their matching outputs in call order. Hook-injected non-tool context is preserved after the complete batch, so the earlier #1292 single-call repair remains intact without splitting the second parallel call away from its plaintext reasoning. Tolerant providers keep their original input order, and duplicate/backwards histories are left unchanged rather than guessed.
Exact head:
4602b1df5e9316bb62965d19bdae1f43c70b8e77.Validation:
origin/dev@849ab5e35in this live-service environment.Review is requested from @lidge-jun and @Wibias. I am leaving both the PR draft and this issue open until independent review, exact-head CI, and integration.
0disoft commentedon Aug 11, 2026
Additional A/B evidence from the same Codex Desktop thread narrows this to the native Responses path.
After another failure on:
I switched only the provider/model selector to:
The task then continued through repeated tool-call turns without this error.
Sanitized OpenCodex usage rows for the same conversation show the protocol difference:
deepseek/deepseek-v4-flash->adapter: openai-responsesopencode-go/deepseek-v4-flash->adapter: openai-chatBoth routes used
requestedEffort: high. This makes a model-level or Codex-thread-level failure unlikely and points specifically to reasoning replay/state reconstruction on the built-in DeepSeek native Responses route. The OpenAI Chat translation path preserves or reconstructsreasoning_contentsuccessfully for the equivalent continuation.Ingwannu commentedon Aug 11, 2026
CI follow-up: draft PR #1479 now has exact head
ec5b995b; its second commit is empty and only retriggers a GitHub Actions run object that was stuck after every job had ended.All Linux shards, gates/typecheck/privacy, API/storage, three-OS keyring, and three-OS npm-global jobs passed. The macOS full-suite job had one unrelated existing 5-second timing-test overrun, then reproduced the same Bun 1.3.14 native SIGSEGV / exit 133 twice. The failing test/runtime paths are outside this diff, and no product assertion related to the DeepSeek parallel replay fix failed.
The additional native Responses versus OpenAI Chat A/B above is consistent with the diagnosed boundary and does not change the patch scope. The issue and PR remain open/draft until independent review and integration; no merge or
mainchange has been made.Wibias commentedon Aug 11, 2026
Fixed by #1501