Repository navigation
[Bug]: Codex Desktop receives response text but the turn never completes over WebSocket in 2.7.43/2.8.0 #864
Description
Client or integration
Codex Desktop for Windows
Area
Responses API transport / WebSocket lifecycle
Summary
After upgrading OpenCodex from 2.7.39 to 2.7.43 / 2.8.0, responses routed from Codex Desktop through the local OpenCodex proxy to an OpenAI-compatible provider are generated successfully, but the Desktop turn does not reliably transition to the completed state.
With websockets: true, the assistant text appears in Codex Desktop, but the activity spinner and stop button remain active indefinitely. The user must stop the turn manually.
With websockets: false, the upstream HTTP SSE response returns status 200 and contains a complete event stream, but Codex Desktop remains on Thinking and does not render the new assistant response.
Rolling OpenCodex back to 2.7.39 and keeping websockets: true restores normal response completion.
Version
OpenCodex 2.7.43 and 2.8.0 are affected. OpenCodex 2.7.39 is the known working rollback version.
Operating system
Windows 10 Home 22H2, version 10.0.19045, build 19045, x64.
Provider and model
OpenAI-compatible Responses API provider at https://llm.ability.associates/v1, using ability/gpt-5.5 in Codex Desktop.
Environment details
PowerShell: 5.1.19041.6456
Codex Desktop: 26.727.4816.0 x64
Codex Desktop embedded app-server/CLI: 0.146.0-alpha.9.2
Global Codex CLI: 0.143.0
Local OpenCodex proxy: 127.0.0.1:10100
OpenCodex service: Windows scheduled task, running
Provider adapter: OpenAI-compatible Responses API
Reproduction
- Install OpenCodex
2.7.43or2.8.0. - Set
websocketstotruein the OpenCodex configuration. - Run
ocx ensureorocx restartand confirm that the proxy is listening on port 10100. - Completely restart Codex Desktop.
- Select
ability/gpt-5.5. - Send a short prompt such as
What model are you?. - Observe that the assistant response text is rendered.
- Observe that the spinner and stop button remain active and the turn never completes automatically.
- Stop the turn manually; the already generated response remains visible.
Alternative transport test:
- Set
websocketstofalse. - Restart OpenCodex and Codex Desktop.
- Send the same short prompt.
- The upstream request returns HTTP 200 with
text/event-stream, but Codex Desktop remains onThinkingand does not render the assistant response.
Expected behavior
Codex Desktop should receive the final lifecycle event and transition the turn to completed after the upstream response finishes, regardless of whether the OpenCodex proxy uses WebSocket or HTTP SSE transport.
Observed behavior
websockets: true
- The provider receives
POST /v1/responses. - The provider returns HTTP 200 quickly.
- Assistant output text is displayed in Codex Desktop.
- The turn remains active indefinitely.
- The Desktop UI continues showing the spinner and stop button.
- Manual stop is required.
websockets: false
- The provider receives the request and returns a valid HTTP 200 SSE response.
- The raw stream completes.
- Codex Desktop does not display the response and remains on
Thinking.
Isolation and supporting evidence
- The provider/VPS receives the request normally and returns status 200 within a few seconds.
- A raw HTTP request through the local OpenCodex proxy completes in approximately 2.2 seconds.
- The raw SSE stream contains the normal completion sequence:
response.created
response.output_text.done
response.output_item.done
response.completed
- In the failing WebSocket case, Codex Desktop logs include:
websocket reuse properties didn't match
- Output events reach the Desktop, but the turn does not appear to receive or recognize the final
response.completedlifecycle event. - Rolling back Codex Desktop itself to a previously working Desktop package did not resolve the problem while the newer OpenCodex version remained installed.
- Cold restarts and reinstalling the OpenCodex background service did not resolve it.
- Rolling OpenCodex back to
2.7.39, withwebsockets: true, restored normal turn completion.
This suggests a regression in the OpenCodex Responses API transport bridge, final-event forwarding, or WebSocket connection reuse logic introduced after 2.7.39. The provider appears to finish the response correctly.
Version timeline
Working: OpenCodex 2.7.39 + websockets enabled
Failing: OpenCodex 2.7.43 + websockets enabled
Failing: OpenCodex 2.8.0 + websockets enabled
Worse fallback behavior: newer OpenCodex + websockets disabled
Working again: rollback to OpenCodex 2.7.39 + websockets enabled
Redacted configuration
{
"websockets": true,
"providers": {
"ability": {
"baseUrl": "https://llm.ability.associates/v1",
"models": ["gpt-5.5"]
}
}
}Authentication values, tokens, account details, request bodies, and unrelated configuration have been removed.
Related issue check
Issue #857 concerns stale model catalogs and the spawn_agent allowlist. It appears to be a different lifecycle problem and does not cover this missing turn-completion behavior.
Checks
- I searched existing issues before submitting.
- I reproduced the behavior with short prompts.
- I compared WebSocket and HTTP SSE transport behavior.
- I verified that the upstream provider completed successfully.
- I verified that rollback to
2.7.39restores normal behavior. - I removed secrets, tokens, account details, credentials, and personal request content.
Activity
github-actions commentedon Aug 1, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
github-actions commentedon Aug 1, 2026
Automated translation bookkeeping — detected language: unknown.
test(server): red regression for #864 — inline rewrite in the eager r…
fix(server): route win32 rewrite traffic through the eager relay (#864)
fix(server): preserve unchanged blocks, order decoder tail, release r…
test(server): make the decoder-tail regression exercise the actual fl…
lidge-jun commentedon Aug 2, 2026
Fixed on
dev(merge 808b885). Root cause: v2.7.43's image_gen aliasing makesneedsClientRewritetrue for Desktop-shaped requests, which pushed Windows onto the tee()+JS-pull SSE chain — the exact Bun#32111-unsafe path that loses the terminal block (text arrives, the turn never completes). Windows rewrite traffic now uses the eager single-reader relay with the payload rewrite applied inline (unchanged blocks pass byte-identical; budget released on every teardown path). The Windows CI leg on the merged SHA is green: https://github.com/lidge-jun/opencodex/actions/runs/30733116558 — scheduled for the next release train. If you can still reproduce a stalled turn on this build, a client-facing frame capture would let us reopen with the exact event sequence.spiritofmoon commentedon Aug 5, 2026
Additional macOS reproduction on OpenCodex 2.10.0 / Codex Desktop embedded CLI 0.147.0-alpha.1.2.
Environment:
codex-cli 0.147.0-alpha.1.2xingnian/gpt-5.6-luna127.0.0.1:10100127.0.0.1:7897Observed behavior:
status: 200terminalStatus: completedcloseReason: terminaltransportPhase: terminal_sseterminalSource: upstreamThinkingindefinitely and requires manual stop.Transport isolation:
websockets: false, the embedded Codex CLI attempts:ws://127.0.0.1:10100/v1/responsesand reports
HTTP error: 426 Upgrade Required; output stops afterthread.startedandturn.started, with no successful HTTP fallback.websockets: true, the request reaches OpenCodex and the provider returns 200, but the CLI/Desktop can still remain blocked afterthread.started/turn.started.Running
codex exec --ephemeral --json -m 'xingnian/gpt-5.6-luna' ...with that provider returns:{"type":"thread.started", ...} {"type":"turn.started"} {"type":"item.completed","item":{"type":"agent_message","text":"你好"}} {"type":"turn.completed", ...}However, after restoring the routed model catalog so the same model appears in Codex Desktop, Desktop still remains on
Thinkingeven though OpenCodex reports the request completed and the conversation title is generated.This suggests two interacting compatibility problems:
Network/TLS was separately verified: an explicit CONNECT through the outbound proxy reaches
chatgpt.com, certificate verification succeeds, and returns HTTP/2 405 for an unauthenticated GET, so this reproduction is not caused by the earlier TLS/proxy issue.test(ws-upstream): hold the eager-relay marker to the win32 rule, not…
test(server): red regression for lidge-jun#864 — inline rewrite in th…
fix(server): route win32 rewrite traffic through the eager relay (lid…
fix(server): preserve unchanged blocks, order decoder tail, release r…
test(server): make the decoder-tail regression exercise the actual fl…
test(ws-upstream): hold the eager-relay marker to the win32 rule, not…
test: repair the two source-anchor and platform assumptions CI found
feat(spend): require one writer lease per state directory for the spe…