Skip to content

[Bug]: DeepSeek V4 Flash Responses continuation still drops reasoning_text on 2.12.0 #1477

Closed

Description

@0disoft

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

  1. Start a Codex Desktop turn using deepseek/deepseek-v4-flash with High reasoning.
  2. Give it a coding task that requires several shell/tool calls.
  3. Let the model issue tools and return their results.
  4. 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.ts merges that registry value when the saved provider does not explicitly override it;
  • openai-responses.ts preserves 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

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

  1. github-actions commented on Aug 11, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  2. changed the title [-][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[/+] on Aug 11, 2026
  3. c3right commented on Aug 11, 2026

    @c3right

    Additional controlled reproduction narrows the trigger substantially.

    Environment

    • OS: Windows 10 Pro
    • ChatGPT / Codex Desktop: 26.803.41515 (Powered by Codex & OWL, released 2026-08-07)
    • OpenCodex: 2.12.0
    • Multi-agent surface: v1
    • Parent model: gpt-5.6-luna, reasoning effort high
    • Child model: deepseek/deepseek-v4-flash
    • Child reasoning effort: max
    • Inbound protocol: responses
    • OpenCodex adapter: openai-responses

    Minimal control matrix

    Using the same DeepSeek sub-agent route:

    • no tool call -> completed
    • 1 tool call -> completed
    • 2 strictly sequential tool calls -> completed
    • 5 strictly sequential tool calls -> completed
    • 2 tool calls emitted in the same model turn / before either result is awaited -> fails reproducibly with the same upstream 400

    The two-tool sequential case was verified from the child rollout: call #2 was emitted only after the function_call_output for 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, same max effort, and the same simple two-file read task.

    Baseline behavior (no parallel-tool restriction):

    • spawn_agent: succeeds
    • child later ends errored
    • upstream error: The reasoning_text in the thinking mode must be passed back to the API.

    Serial variant changed only the tool-use policy:

    • at most one tool call per model response turn
    • wait for that tool result before emitting another tool call
    • never emit same-turn / parallel multiple tool calls

    Result:

    • spawn_agent: succeeds
    • child ends completed
    • both files read successfully
    • no API error

    This 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 deepseek-v4-flash
    • effort: max
    • adapter: openai-responses
    • status: 200
    • reasoningOutputTokens: 78

    Continuation request:

    • same conversation
    • DeepSeek deepseek-v4-flash
    • effort: max
    • adapter: openai-responses
    • status: 400
    • sendCount: 1
    • recoveryKinds: []
    • upstream error: 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.

  4. Ingwannu commented on Aug 11, 2026

    @Ingwannu
    Owner

    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

    reasoning
    function_call(A)
    function_call(B)
    function_call_output(A)
    function_call_output(B)
    

    the current per-pair normalizer rewrites it as reasoning -> call A -> output A -> call B -> output B. DeepSeek documents that reasoning and function_call items 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 dev by 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.

  5. Ingwannu commented on Aug 11, 2026

    @Ingwannu
    Owner

    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:

    • 296/296 focused adapter, DeepSeek replay, config, and registry tests passed.
    • TypeScript typecheck and privacy scan passed.
    • The documentation site built all 265 pages.
    • The full suite completed with 11,049 passed, 11 skipped, and two failures outside this diff: the bridge stall timing test passed in isolation, and the Unix shim token test fails identically on clean origin/dev@849ab5e35 in 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.

  6. 0disoft commented on Aug 11, 2026

    @0disoft
    ContributorAuthor

    Additional A/B evidence from the same Codex Desktop thread narrows this to the native Responses path.

    After another failure on:

    deepseek/deepseek-v4-flash (High)
    The `reasoning_text` in the thinking mode must be passed back to the API.
    

    I switched only the provider/model selector to:

    opencode-go/deepseek-v4-flash (High)
    

    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-responses
    • opencode-go/deepseek-v4-flash -> adapter: openai-chat
    • after switching to OpenCode Go, multiple consecutive continuation requests returned HTTP 200

    Both 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 reconstructs reasoning_content successfully for the equivalent continuation.

  7. Ingwannu commented on Aug 11, 2026

    @Ingwannu
    Owner

    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 main change has been made.

  8. self-assigned this
    on Aug 11, 2026
  9. Wibias commented on Aug 11, 2026

    @Wibias
    Contributor

    Fixed by #1501

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingtoolstool_calls, MCP, web-search / sidecar tools

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions