Repository navigation
[Bug] DeepSeek V4 Flash Responses route stalls after tool calls #875
Description
Activity
Automated translation bookkeeping — detected language: unknown.
- addedtoolstool_calls, MCP, web-search / sidecar toolstool_calls, MCP, web-search / sidecar tools
on Aug 2, 2026 Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
Michael-Han0608 commented
on Aug 2, 2026 AuthorMore actionsReproduced again on OpenCodex v2.10.0. The behavior appears unchanged from the original report:
- plain-text turns may complete normally;
- when the model emits tool calls, the calls can be generated and executed;
- the upstream DeepSeek Responses request still returns HTTP 200 with non-zero output/reasoning tokens;
- no corresponding
function_call_outputor follow-up Responses request is observed for the same turn; - Codex App therefore receives no final visible answer and the turn appears to stall.
The v2.10.0 routing change does not appear to address this continuation failure. It keeps Codex-originated requests on the native Responses path, while Chat/Anthropic-originated requests use the provider-wide Chat route. The remaining defect therefore seems specific to the native Responses tool-call continuation or reasoning-history replay, rather than model selection or initial wire routing.
For reference, #808 only established
deepseek-v4-flash→openai-responsesas the default route and verified a comparatively simple tool loop. The failure here still appears with longer or multi-tool turns.Triage verdict (wt3 lane, branch codex/wt3-exec)
We split this into a local defect (fixed) and an unexplained residual (still open).
Local defect — found and fixed. On continuation requests, ocx preserved reasoning items in its continuation store (
src/responses/state.ts:699,:806,:837) and through DeepSeek's stateless cleanup (src/adapters/openai-responses.ts:1003), but thensanitizeReasoningInputContent()(src/adapters/openai-responses.ts:35) blanked every non-empty reasoning item'scontentto[]— for EVERY Responses provider, although that rule exists only for the ChatGPT native backend. DeepSeek's Responses API accepts plaintext reasoning replay (its compatibility guide merges reasoning items into the adjacent assistant message), so every continuation reached DeepSeek with an emptied reasoning item. The fix scopes the sanitizer via a provider-levelpreserveResponsesReasoningContentcapability (registry default on for DeepSeek;ocxr1proxy envelopes are still stripped), with regression coverage intests/deepseek-reasoning-replay.test.ts.Ruled out as the cause:
service_tier. DeepSeek's official Responses docs listservice_tieras unsupported but state unsupported Responses parameters are silently ignored — so it cannot explain a stall. We still capability-gated it fail-closed (supportsServiceTier, PR #860's reviewed semantics) as compatibility hygiene, but that is not this bug.Residual — not explained locally. The "no follow-up HTTP request sent at all" observation cannot be produced by any ocx code path we found: the reasoning defect only fires once a follow-up request REACHES the proxy. If no request was sent, the failure is upstream of us (client/SSE handoff) or in the serving path (external stall reports for V4-Flash are on NVIDIA NIM / vLLM, not hosted api.deepseek.com).
What would close this issue: a repro against the fixed build showing whether the follow-up request now (a) is sent and succeeds — done; (b) is sent and 400s — new evidence, send us the status + body; or (c) is still never sent — that pins the residual to the client/SSE handoff, and a capture of the client-side stream would be the next evidence we need.
Leaving open pending that confirmation; the local half ships with the wt3 branch.
- added a commit that references this issue
on Aug 2, 2026 - added a commit that references this issue
on Aug 2, 2026 Both halves fixed in #892. (1) Reasoning replay: the passthrough sanitizer no longer blanks reasoning content for DeepSeek — the registry seeds
preserveResponsesReasoningContent: truefor the DeepSeek entry (src/providers/registry.ts:1001) and the adapter honors it, with regression coverage in tests/deepseek-reasoning-replay.test.ts citing this issue. (2) Codex Desktop WebSocket terminal event: WS-inbound DeepSeek V4 Flash turns are answered with bounded upstream JSON (registry-scoped hint) that the WS bridge reframes into a complete event sequence, so the turn can no longer stall waiting for a terminal event that never arrives. Note: PR #906 proposed the same proxy-half fix via a per-model list; closed as a functional duplicate of the landed provider-level flag.Michael-Han0608 commented
on Aug 3, 2026 AuthorMore actionsI tested the
codex/wt3-execbranch at commit0b30283b6db205a17b09682632d2f731ac1f1e14.Using
deepseek/deepseek-v4-flashin Codex Desktop, I sent a request requiring multiple tool calls. The initial DeepSeek Responses request returned HTTP 200 with non-zero output and reasoning tokens, but the turn remained pending and never produced a final answer.I did not observe a follow-up DeepSeek Responses request carrying the tool results for the same turn.
On this tested commit, the behavior therefore matches case (c) from the triage comment: the initial request succeeds, but no follow-up request is sent and the Codex client remains waiting.
Reopening because the reporter added decisive post-closure evidence: the candidate branch still completes the initial DeepSeek Responses request but sends no
function_call_outputcontinuation request and leaves Codex Desktop pending. That means the residual described here was not fixed by the reasoning-replay and bounded-JSON changes already landed.PR #940 is related but not a complete fix for this built-in path in its current form. It adds an opt-in SSE repair, while the built-in DeepSeek preset does not enable that option and Codex App WebSocket turns use the bounded JSON-to-WebSocket path instead of the SSE rewrite. Keeping #875 as the canonical tool-loop issue so the existing reproduction and diagnosis stay together. #946 is being closed as its duplicate.
11 remaining items
- added a commit that references this issue
on Aug 5, 2026 Fixed on
devin #1026 (merged asc1e9a204b).The bounded-JSON reliability policy was gated on
inboundTransport === "websocket", so ordinary HTTP kept requesting a stream — exactly thewebsockets:false, bodyKind:ssereproduction reported here. The policy is now transport-neutral: for a model that declares it, the proxy asks upstream for bounded JSON and reframes the completed response as a canonical terminal SSE sequence, with exactly one[DONE], so a streaming client commits the turn instead of hanging.It also composes with the item-ID repair rather than bypassing it, which review caught as a way this policy could silently disable that fix.
Thanks to @Michael-Han0608 (#1006) — the diagnosis and the core mechanism come from that PR.
Not in a release yet. This is on
dev, andmainis atv2.10.0. It ships in the next release.Reacted by Michael- added a commit that references this issue
on Aug 7, 2026 - added 9 commits that reference this issue
on Sep 17, 2026
Thanks for the report. Include a minimal reproduction and exact environment details so we can confirm and fix the problem quickly.
Client or integration
Codex App
Area
Tools / MCP / web search
Summary
DeepSeek V4 Flash requests routed through the native Responses adapter can stall after tool calls. The upstream request returns HTTP 200 and non-zero output/reasoning tokens, but OpenCodex does not continue with the corresponding tool outputs, so no final visible answer is produced.
Plain-text turns without tools can complete normally. DeepSeek V4 Pro through the
openai-chatroute works normally under the same local setup.Update: reproduced on OpenCodex 2.10.0
The same behavior remains reproducible after upgrading to OpenCodex 2.10.0:
function_call_outputor follow-up Responses request is observed for the same turn;The v2.10.0 routing change does not appear to address this continuation failure. Codex-originated requests still use the native Responses path, so the remaining defect appears to be in the Responses tool-call continuation, event handoff, or reasoning-history replay rather than initial model routing.
Reproduction
deepseek/deepseek-v4-flash.function_call_outputor follow-up request is sent for the same conversation, and no final assistant answer appears.openai-chatroute does not reproduce the problem.The exact trigger is not yet confirmed; it may involve the native Responses event handoff, multiple tool calls, or reasoning-history replay.
Version
Originally reproduced on OpenCodex 2.9.1; reproduced again on OpenCodex 2.10.0.
Operating system
macOS
Provider and model
Official DeepSeek API /
deepseek/deepseek-v4-flashLogs or error output
Relevant local usage records:
ocx-msbd27ow-17: HTTP 200, output tokens 568, reasoning tokens 359ocx-msbdeyhv-1v: HTTP 200, output tokens 401, reasoning tokens 215ocx-msbdfnwj-1x: HTTP 200, output tokens 113, reasoning tokens 36The first two responses contained two completed
exec_commandfunction calls, but no correspondingfunction_call_outputor follow-up request was observed for the same conversation.service_tierwasdefault; nopriority, 400, or 502 error was recorded.Screenshots and supporting files
The issue was reproduced three times after a long processing period with no visible output. Local usage records are available above.
Redacted configuration
The relevant setup uses the official DeepSeek provider, with
deepseek/deepseek-v4-flashrouted through the native Responses adapter. No secrets or credentials are included.The current Responses adapter appears to sanitize
reasoning.contentfor all Responses providers. DeepSeek's Responses API supports plain-textreasoning_textinput, so this may also affect subsequent tool-call replay. This observation is not yet confirmed as the primary trigger.Checks
References