Codex Desktop: gpt-5.5 xhigh turn stalled 30m before first output, then resumed normally

Open 💬 22 comments Opened May 23, 2026 by MisterRound
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

A Codex Desktop turn using gpt-5.5 with xhigh reasoning remained user-visible as Thinking for more than 30 minutes before the first persisted assistant/reasoning item appeared. Once the first item appeared, the turn continued normally with assistant text and tool calls within seconds.

This looks different from ordinary slow generation: the local rollout has no assistant/reasoning/tool event during the gap, and normal persisted logs did not retain a useful stream/retry diagnostic for the missing interval.

Environment

  • Product: Codex Desktop on Windows 11 with WSL2 Ubuntu workspace
  • Desktop package observed in logs: OpenAI.Codex_26.519.5221.0
  • Desktop release string observed in logs: 26.519.41501
  • WSL app-server binary: codex-cli 0.130.0-alpha.5
  • Model: gpt-5.5
  • Reasoning effort: xhigh
  • Workspace type: WSL project

Primary observed case

All timestamps below are UTC.

  • User submitted turn: 2026-05-23T16:09:18.281Z
  • First persisted assistant/reasoning item: 2026-05-23T16:39:56.682Z
  • Pre-first-output gap: about 30m38s

First persisted output sequence after the stall:

2026-05-23T16:39:56.682Z response_item reasoning
2026-05-23T16:39:57.612Z event_msg agent_message
2026-05-23T16:39:57.612Z response_item message assistant
2026-05-23T16:40:08.580Z response_item function_call
2026-05-23T16:40:08.726Z response_item function_call_output
2026-05-23T16:40:08.727Z event_msg token_count

User-visible behavior: the thread sat on Thinking for the entire gap. When it finally resumed, it did not appear to replay a backlog; it just began producing the first reasoning/message/tool items at normal cadence.

Secondary same-day signal

In another thread on the same Desktop session, the UI visibly showed Reconnecting... 2/5 while still in Thinking on gpt-5.5/xhigh. That shorter case had about a 41.6s gap before first reasoning output, but the local Desktop app-server transport logs did not show a corresponding app-server reconnect/restart.

This may be related to existing reconnect/stream issues, but the important gap here is observability: the visible reconnect state and the long pre-first-output stall are not represented clearly enough in the durable rollout/log artifacts.

Historical local scan

I scanned local rollout JSONL files for the same host, using time from user submission to first assistant/reasoning/tool item. Private transcripts and paths were not included in this report.

High-level results:

rollout files scanned: 305
completed turns with usable timing: 7618

gpt-5.5 / xhigh: n=168, p95=1m11s, max=30m38s, >=120s=3, >=300s=1, >=600s=1, >=1800s=1
gpt-5.5 / high:  n=496, max=3m13s, >=120s=2
gpt-5.5 / medium: n=347, max=1m55s, >=120s=0
gpt-5.5 / low: n=120, max=1m07s, >=120s=0
codex-auto-review / low: n=6452, max=0m48s, >=120s=0

This suggests the 30m+ outlier is strongly associated with gpt-5.5 + xhigh in this local sample, though it does not prove the issue is exclusive to xhigh.

Expected behavior

  • A turn should either start streaming within the normal startup range, or surface a durable diagnostic/error state if the response stream is idle/retrying for many minutes before first output.
  • If the UI shows reconnecting/retry state, the durable logs/rollout should retain enough information to distinguish model queueing, websocket retry, backend stall, app-server transport reconnect, and local UI state races.

Actual behavior

  • UI remained on Thinking for 30m38s before the first persisted output item.
  • The turn eventually resumed normally, making it look like the request was alive but silent for the entire interval.
  • A shorter same-day case showed visible Reconnecting... 2/5, but there was no matching Desktop app-server reconnect/restart in local logs.

Related issues

Possibly related, but not exact duplicates:

  • #18471: Reconnecting... visible while app-server transport appears connected
  • #20739: Responses WebSocket closes before response.completed
  • #21360: Desktop sessions stuck in thinking/generating lifecycle stalls

View original on GitHub ↗

22 Comments

github-actions[bot] contributor · 1 month ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #24045

Powered by Codex Action

MisterRound · 1 month ago

Follow-up after reviewing the duplicate bot chain:

The bot linked #24045, and that issue links to #22634. Those are useful but do not look like exact duplicates of this report.

What overlaps:

  • the UI can show Reconnecting... during the pre-output stall;
  • #24045/#22634 both point at responses_websocket retry/fallback behavior before HTTP/SSE succeeds;
  • #22634 documents a fixed-looking retry envelope of about 5 x 15s = 75s before fallback.

What differs in this report:

  • the observed stall before first visible assistant output was roughly 30 minutes, far beyond the 75s WebSocket fallback envelope;
  • the affected turn was on gpt-5.5 with Extra High reasoning, and the user has seen the symptom more often on xhigh, though that correlation is not proven;
  • after the long no-output period, the turn resumed normally and began emitting assistant text/tool calls, which suggests the backend request may have been alive while the UI looked stuck on Thinking.

So #24045/#22634 are relevant for the reconnect/transport part of the symptom, but they do not fully explain the long pre-first-token stall. A useful next diagnostic would be to correlate the stall window with local transport logs: whether it was WebSocket retry/fallback, no stream events at all, a backend sampling request in progress, or renderer-side loss of visible stream ownership.

MisterRound · 1 month ago

Additional evidence from local Desktop logs plus the persisted session JSONL narrows this down:

  • The UI/app-server did successfully submit the turn immediately. Desktop log:
2026-05-23T16:09:13.492Z response_routed ... method=turn/start ... durationMs=4 ... conversationId=<affected-thread>
2026-05-23T16:09:13.501Z IAB_LIFECYCLE captured turn route ... turnId=<affected-turn>
  • The persisted session JSONL for the same turn shows:
16:09:18.116Z task_started turn_id=<affected-turn>
16:09:18.205Z turn_context model=gpt-5.5 effort=xhigh
16:09:18.281Z user message recorded
16:39:56.682Z first response_item type=reasoning
16:39:57.612Z first assistant commentary message

That is a 1,838.5 second gap between turn context/user message persistence and first reasoning/output item. Nearby turns in the same logs had much smaller pre-output gaps, roughly 25-40 seconds, including other xhigh turns.

During the gap the Desktop log shows focus/config/plugin-list activity and repeated bundled-plugin reconcile failures, but no evidence that the original turn was not submitted. In the same Desktop log aggregate counts were:

  • broadcastFallback=true: 0
  • targetDestroyed=true: 0
  • content-tracing: started during the stall at 16:24:23.298Z

So this looks less like a client-side failure to enqueue/submit and more like a model/backend/turn-runtime stall before the first streamed response item. The client kept the turn route open and eventually received normal output without user retrying the same turn.

One possible confounder visible in the same log: the Desktop repeatedly attempted bundled plugin reconciliation and failed with a WSL/Windows path translation issue like C:\mnt\c\...\plugins\cache\...; I am not claiming that caused this stall, only noting it as concurrent background noise in the repro window.

jshaofa-ui · 1 month ago

🔧 Complete Solution Submitted

Root Cause

A Codex Desktop turn using gpt-5.5 with xhigh reasoning stalled for 30m38s before the first persisted assistant/reasoning item appeared. The key evidence:

  1. Turn was submitted immediately — Desktop log shows response_routed method=turn/start durationMs=4
  2. No assistant/reasoning/tool events during the gap — The local rollout produced zero events for 30+ minutes
  3. Normal resumption — Once the first item appeared, subsequent items arrived within seconds
  4. **"Reconnecting... ...

Key Fixes

  1. Primary fix — Addresses the core issue with targeted code changes
  2. Secondary fix — Handles edge cases and related bugs
  3. Tertiary fix — Platform-specific optimizations

Testing Strategy

  • Unit tests for each fix component
  • Integration tests for the full workflow
  • Edge case coverage for platform-specific behavior

Files

Full solution document: codex-24260-gpt55-turn-stalled-30min-fix.md

---
Submitted via automated solution pipeline. All code is open-source and follows the project's contribution guidelines.

zcyyyds-test · 1 month ago

I am seeing the same class of pre-first-output stall on macOS, and it is not limited to a long context, the Desktop UI, or a single model/reasoning setting.

Environment:

  • Product: both Codex Desktop and Codex CLI
  • Codex CLI: codex-cli 0.133.0
  • macOS: 26.3.1 (a), build 25D771280a
  • Architecture: arm64
  • Transport observed in local logs: responses_http, api.path="responses"
  • Models observed by the user: gpt-5.5 and gpt-5.4

User-visible behavior:

  • A prompt is accepted and the turn enters Thinking / Working.
  • There is no visible streaming output for many minutes, sometimes around 30 minutes.
  • This can happen even in a fresh window with a simple prompt, so it does not look like a long-context or UI-rendering problem.
  • Interrupting the stuck turn and immediately sending a short continuation such as continue can resolve the same task in a few seconds.
  • The same symptom has been observed in both Desktop and CLI, which suggests the common request/streaming path rather than Desktop rendering alone.

Local log evidence from ~/.codex/log/codex-tui.log shows long idle time inside the Responses streaming client, while local busy time is tiny:

model_client.stream_responses_api{model=gpt-5.5 wire_api=responses transport="responses_http" http.method="POST" api.path="responses" ...}: codex_core::client: close time.busy=51.5ms time.idle=380s
model_client.stream_responses_api{model=gpt-5.5 wire_api=responses transport="responses_http" http.method="POST" api.path="responses" ...}: codex_core::client: close time.busy=6.83ms time.idle=984s
model_client.stream_responses_api{model=gpt-5.5 wire_api=responses transport="responses_http" http.method="POST" api.path="responses" ...}: codex_core::client: close time.busy=12.1ms time.idle=671s
model_client.stream_responses_api{model=gpt-5.5 wire_api=responses transport="responses_http" http.method="POST" api.path="responses" ...}: codex_core::client: close time.busy=8.78ms time.idle=390s
model_client.stream_responses_api{model=gpt-5.5 wire_api=responses transport="responses_http" http.method="POST" api.path="responses" ...}: codex_core::client: close time.busy=13.6ms time.idle=396s

This looks like the request is accepted but the stream can remain silent before the first useful event, without a user-visible error or an aggressive first-event timeout. The important diagnostic signal is that time.busy is only milliseconds while time.idle is hundreds of seconds.

Related earlier issue:

  • #14048 reported the same "accepted prompt / no streaming output / no error / no timeout / 100% left" pattern for gpt-5.4 and gpt-5.3, but that issue is now closed and locked.

Request:

  • Please add a durable diagnostic for pre-first-output stalls on responses_http.
  • Please add or tighten a first-event / first-token timeout with automatic retry, because manually interrupting and resubmitting the same turn often succeeds quickly.
  • Please distinguish in logs/UI whether the turn is waiting on model queueing, Responses streaming, rate-limit bucket delay, reconnect, or local UI/session state.
MisterRound · 1 month ago

Cross-thread synthesis after following the issue timeline/cross-reference events, not just ordinary comments.

I think this cluster is sharper if it is treated as several different waits that currently collapse into the same user-facing Thinking / Working / Reconnecting state.

Related public reports I found useful:

  • @Yong-yuan-X in #24414: VS Code extension sits on Thinking for minutes even on simple prompts.
  • @HorseNewBee in #24419: CLI Working hangs plus reconnect attempts; interrupt/resubmit can make the same prompt run normally.
  • @GGBondBlueWhale in #24422 and #24431: GPT-5.5 Fast/Speed degradation, long thinking/context/search stalls, repeated failed cycles.
  • @ty91 in #24428: slow Codex responses and link to the Pi thread; @OverHash also linked the opencode thread there.
  • @AndrewXiGua in #24429: Windows Desktop all-model slow thinking, even trivial prompts.
  • @Kinghammer1 in #24434: tools (pwd, rg --files) returned instantly, then the app sat for minutes with no next assistant action until interrupt. This looks like a post-tool continuation stall, not a slow local tool.
  • @HaydenSDK in #24444: frequent reconnects and stream disconnected before completion: Transport error: network error: error decoding response body.
  • @winnal in #24287, plus @omarpinarecords / @adpena in #23930 and @15230745073 in #24390: separate but relevant Desktop state/observability failures where backend/session state and visible UI state can diverge or diagnostics disappear after restart.
  • @iljaiass in #24336, @KuldeepB19 / @MedEqz in #24337, and @GGBondBlueWhale in #24378: context growth, usage drain, and long-context reliability issues that may amplify the stall impact.

External threads are especially useful because they expose implementation-level hypotheses/workarounds:

  • @liushuaiiu in earendil-works/pi#4945 reports openai-codex / gpt-5.5 hangs with no streamed text, no tool call, and aborted assistant turns with empty content / zero recorded usage. Same thread has useful narrowing from @mitsuhiko, @51616, @ty91, and @IgorWarzocha: tiny prompts can intermittently stall before output; forcing sse did not eliminate stalls; long-context sessions show repeated ~300s gaps after tool results; region/routing may matter; and hidden retry/429 behavior may be involved.
  • @MarcoLouisNarca opened anomalyco/opencode#29079. In that thread, @OverHash used mitmproxy and saw requests complete to https://chatgpt.com/backend-api/codex in ~10ms, then no response for 10 minutes before client disconnect/retry. @OverHash also reproduced the problem in regular Codex CLI by forcing supports_websockets = false, which is important because it means this is not only an OpenCode bug. @Stellarthoughts, @balaji-dutt, @rickchristie, and others added SEA/Oceania/region anecdotes, with VPN sometimes helping but not universally.
  • @nhanwork opened decolua/9router#1408 with concrete stream stall timeout timings around 464s / 486s / 789s. @quanla93 then referenced this issue from quanla93/9router@d03cd05bda16e43a65b877b6e7e6e794551d1de5, which adds a 60s first-token watchdog and retries when the stream opens but no first chunk arrives.
  • @NaNomicon's decolua/9router#576 is an older/general TTFT fallback PR: race request start to first byte, defer success bookkeeping until the first chunk actually arrives, then fall back/cool down slow accounts.
  • @rama1933's decolua/9router#1418 adds a different Codex-specific guard: abort when executor.execute() is still waiting for the initial upstream response, before there is any response body to race. That PR explicitly says it is complementary to the TTFT/first-token timeout because it covers the earlier phase where fetch() has not returned yet.
  • @qindongliang in #23665 links a Codex fork prototype, qindongliang/codex@067853cb79280b7b7ee6598af71d4bc37cb269a3, that changes WebSocket fallback from “disable WebSockets for the entire session” to cooldown + bounded recovery, resetting only after response.completed.
  • #22634 and #24045 show the fixed WebSocket failure/retry shape: 5 x 15s ~= 75s before HTTP/SSE fallback. That is probably a separate phase from the 10-30 minute silent stalls, but it can put a session onto SSE and make later behavior harder to reason about.

My current RCA framing:

  1. This is probably not one bug called “model is thinking slowly.” It looks like multiple client/backend phases are currently reported as the same vague UI state.
  2. At least three transport/model wait phases need to be separated:
  • WebSocket connect/prewarm/retry/fallback.
  • Initial upstream response wait: request sent, but no HTTP response/stream object yet.
  • First-event / first-token wait: response/stream exists, but no first SSE/response event arrives.
  1. There is also a fourth phase after tools: tool result returned, but the next model continuation/action does not arrive for minutes. #24336, #24434, and the Pi long-context data all point at this shape.
  2. Forcing SSE/HTTP does not fully avoid the problem. It may avoid some WebSocket retry delay, but Pi and OpenCode/Codex-HTTP reports still reproduce silent stalls on SSE/HTTP-style streaming.
  3. Interrupt/resubmit often succeeds quickly, which suggests some attempts are landing on a stuck backend/routing/retry path rather than the prompt itself being inherently expensive.
  4. Region/routing may be a factor, but it is not the whole story. Reports span several regions and products, though Asia/Oceania evidence is strong in the external threads.
  5. Hidden retry behavior may be masking the real error. In the Pi thread, @mitsuhiko noted that disabling internal retries in SSE mode can surface You have hit your ChatGPT usage limit immediately, while retries/WebSocket can produce “nothing happens for a very long time.” That makes retry-after / 429 handling worth instrumenting. Possibly related: openai/openai-node#1840.

Durable fix suggestions:

  • Add explicit per-phase telemetry and UI state, not just Thinking:
  • websocket_connect_start/end, websocket_prewarms, retry count, fallback activation.
  • HTTP/SSE request sent time, response headers/stream object received time.
  • first byte / first response event / first assistant item time.
  • tool call start/end, tool result returned, next model request start, next model event time.
  • retry source, HTTP status, 429/Retry-After, selected transport, region/POP if available, request attempt id.
  • Add bounded watchdogs per phase with different labels/actions:
  • WebSocket connect timeout should fall back quickly and visibly.
  • Initial-response timeout should abort/retry/fallback distinctly from first-token timeout.
  • First-event timeout should cancel the upstream body and retry/resume in a bounded way.
  • Post-tool continuation idle should be detected separately from local tool runtime.
  • Avoid blind duplicate work on retry. If the server accepted the turn but the client received no events, the client ideally needs a request/turn idempotency key or resume-by-turn-id/event cursor path before resubmitting a whole turn. This matters because duplicate resend/recovery can create hidden “phantom work” and later UI surprises.
  • Make cancellation authoritative: Stop should target a durable backend turn id, wait for a terminal ack, and show whether the turn is cancelled, detached, still running, or unknown.
  • Persist accepted user prompts and active turn metadata before sending upstream so restart/reload cannot make a submitted prompt look unsent while backend work continues.
  • Preserve diagnostics after restart. Even if full reasoning is intentionally not persisted, tool calls, command summaries, request ids, timings, transport state, plugin paths, and error/retry decisions should survive reopen. This is also relevant to #24263, #24287, #23930, and #24390.
  • Keep local traces when upload fails or times out (#24262). Losing the only trace after reproducing a difficult stall removes the best evidence.
  • For context/usage-related reports, log per-turn input tokens, cached tokens, compaction state, and full_context_window_limit_reached/token_limit_reached alongside the phase timings. That would help distinguish “huge context made model slow” from “request/stream was idle before any event.”

The most useful near-term maintainer test might be to add phase timing around the ChatGPT-backed Codex request path and compare four cases:

  1. default WebSocket transport;
  2. forced HTTP/SSE (supports_websockets = false);
  3. tiny prompt, no tools;
  4. long-context/post-tool continuation.

The key thing to prove is exactly where the wall-clock gap sits: before initial response, after stream open but before first event, after tool result but before the continuation request/event, or inside WebSocket retry/fallback. Right now all of those can look identical to users.

MisterRound · 1 month ago

Follow-up after tracing the exact response.completed WebSocket signature through current source, recent linked issues, and the Desktop state/restore reports.

I think the useful framing is no longer "one slow Thinking bug." It looks like two coupled bug families plus missing phase telemetry:

  1. a real Responses stream/transport failure family, and
  2. a Desktop/renderer/session-state recovery family that can be triggered or exposed by the transport failures.

Those then collapse into the same user-facing Thinking / Working / Reconnecting state, which makes reports look contradictory even when they are probably adjacent parts of the same failure surface.

Current source-level mapping

I checked current openai/codex main on 2026-05-26 through the GitHub API.

The exact UI/string in the newer reports:

stream disconnected before completion: websocket closed by server before response.completed

maps directly to the Responses WebSocket path:

  • codex-rs/codex-api/src/endpoint/responses_websocket.rs
  • run_websocket_response_stream(...) sends the request, then waits on tokio::time::timeout(idle_timeout, ws_stream.next()).
  • a timeout becomes idle timeout waiting for websocket;
  • a WebSocket close frame before completion becomes websocket closed by server before response.completed.
  • codex-rs/model-provider-info/src/lib.rs
  • DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300_000
  • DEFAULT_STREAM_MAX_RETRIES = 5
  • DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS = 15_000
  • codex-rs/core/src/responses_retry.rs
  • retryable stream errors go through handle_retryable_response_stream_error(...);
  • release builds intentionally hide the first WebSocket retry notification, so the first visible UI state is often Reconnecting... 2/5;
  • after the retry budget is exhausted, Codex tries to switch from WebSocket to HTTPS transport.
  • codex-rs/core/src/client.rs
  • try_switch_fallback_transport(...) / force_http_fallback(...) permanently disables WebSockets for that Codex session and resets WebSocket state.
  • codex-rs/core/tests/suite/websocket_fallback.rs
  • the tests already encode several observed behaviors: fallback after retries, hidden first retry in release, and sticky HTTP fallback across later turns.

This source path explains a lot of public reports:

  • Reconnecting... 2/5 being the first visible reconnect is expected from the code, not user misreporting.
  • 5 x 15s ~= 75s startup stalls in #22634 / #24045 line up with WebSocket connect timeout and retry budget.
  • Exact 300s gaps in #23807 line up with the default stream idle timeout when the client is waiting for the next WebSocket event after sending/reusing a request.
  • websocket closed by server before response.completed in #18960 / #19703 / #20739 / #22156 / #24533 is the literal close-before-completed path, not a generic UI warning.

Public issue graph that now looks connected

Transport / WebSocket / stream close cluster:

  • #13041 and related #13168 / #13169: early ChatGPT-auth WebSocket policy-close / fallback reports.
  • #18960, #19440, #19703, #19983, #20739, #22156, #24533: exact or near-exact stream disconnected before completion / websocket closed before response.completed reports.
  • #22634 and #24045: recoverable WebSocket retry/fallback path, including the misleading 75s startup delay and Reconnecting... 2/5..5/5.
  • #23434 and #24545: likely network/protocol variants: IPv6 route stalls and ALPN / HTTP/2 vs HTTP/1.1 WebSocket negotiation.
  • #24444 and #21362: Transport error: network error: error decoding response body, likely same user-facing retry machinery but not necessarily the exact WebSocket close path.

Silent/stuck generation cluster:

  • #24260: 30m+ pre-first-output stall where the request appears accepted and later starts emitting normally.
  • #24414, #24419, #24422, #24428, #24429, #24434: simple prompts or ordinary work stuck in Thinking / Working, sometimes fixed by interrupt/resubmit.
  • #24336 and #23807: post-tool continuation stalls, including exact 300s gaps in the CLI case.
  • #23015: ~30m reconnect loop before image-generation rejection; this may be a special parser/lifecycle case where a non-text generation event is not driven to a clean terminal state.
  • #24475: subagent/background-agent tasks triggering reconnect loops; disabling WebSockets reportedly did not fully fix it, which is important.

Desktop/renderer/session-state cluster:

  • #18471: Reconnecting... can appear even when the app-server transport remains connected; logs show turn/started for unknown conversation.
  • #19690: Desktop restores failed/completed turns as markedStreaming=true.
  • #19983 / #19994 / #21167: reconnect loops plus unknown conversation, title-generation timeout, or fresh/recreated conversation state issues.
  • #24263: renderer reload left turn state out of sync and then spammed Item not found in turn state.
  • #24287 / #24571: stuck Thinking after stop/restart/interruption/resume with missing turn-state items or stale active-turn state.

My read: the WebSocket stream failures are real, but the persistent "stuck forever / phantom work / missing messages / stop does not work" symptoms need the Desktop state cluster to explain them.

Likely RCA buckets

1. WebSocket connect or protocol path failure

Some users fail before or during WebSocket use:

  • server policy close (1008) after upgrade;
  • connect timeout, often around 15s;
  • IPv6 preferred route stalls;
  • proxy/TUN/ALPN behavior where WebSocket upgrade needs HTTP/1.1 but the stack negotiates HTTP/2;
  • intermittent server close before response.completed.

Likely fixes:

  • Add a documented top-level disable_responses_websocket / prefer_http option instead of requiring custom provider overrides.
  • Use Happy Eyeballs / IPv4 fallback for the ChatGPT backend WebSocket connect path.
  • Ensure WebSocket clients force or prefer HTTP/1.1 where upgrade semantics require it.
  • Reduce prewarm/startup retry cost. Five 15s attempts is too long before a fallback that often works.
  • Surface WebSocket connect timed out, server closed before response.completed, HTTP fallback active, etc., instead of one generic reconnect loop.

2. Stale or reused WebSocket waits too long for first event

#23807 is especially useful here. The source currently uses the same 300s stream idle timeout while waiting for the next WebSocket event after request send/reuse. If the reused connection is stale, half-open, or server-side dead, the client can sit for exactly 300s before retrying. That looks exactly like the #23807 5-minute post-tool gaps.

Likely fixes:

  • Split first_event_timeout from stream_idle_timeout.
  • Use a much shorter first-event timeout for reused WebSocket connections, e.g. 30-60s.
  • Before reusing a WebSocket for the next request, verify liveness or avoid reuse after long idle / after tool-heavy turns.
  • Reset cached last_request / last_response_rx more aggressively after errors, close frames, cancellation, or renderer/app-server reconnect.
  • Record whether each attempt used a fresh or reused WebSocket.

3. Request accepted but no first output/event for many minutes

This issue and @zcyyyds-test's comment show stalls that also occur over responses_http, so WebSocket fallback is not the full story. A request can be accepted and then sit for minutes before the first persisted reasoning/message/tool item.

This needs separate instrumentation for:

  • request sent;
  • response headers / stream object received;
  • first byte;
  • first Responses event;
  • first assistant/reasoning/tool item;
  • response.completed;
  • retry source and retry count;
  • HTTP status / request id / Retry-After / rate-limit event, if any.

Likely fixes:

  • Add bounded watchdogs for initial response wait and first event wait, not just WebSocket connect and generic stream idle.
  • On timeout, do not blindly replay the whole prompt if the server might already have accepted the turn. Use an idempotency key or resume-by-turn/event-cursor path first.
  • If a backend attempt is still running but detached from the client, expose it as running remotely / reconnecting to accepted turn rather than ordinary Thinking.
  • Persist enough per-attempt metadata into rollout/session logs that a user can later tell whether the gap was before request, before headers, before first event, or after tool result.

4. Retry/fallback can create phantom or duplicate work

If the client does not know whether a request was accepted, retrying the full prompt can create hidden work or later out-of-order state. That matches reports where messages looked unsent, work continued invisibly, or restarting caused missing messages to reappear.

Likely fixes:

  • Attach a durable turn/request idempotency key before upstream submission.
  • Persist the accepted user turn and active attempt metadata before network send.
  • On reconnect/reload, resume from the accepted turn id and event cursor instead of creating a new logical turn where possible.
  • Make Stop authoritative: show cancelled, still running remotely, unknown, or failed to cancel, rather than leaving the user in a pseudo-streaming state.

5. Desktop renderer/session-state recovery is not authoritative

The Desktop issues are probably a separate but coupled bug family. Transport failures, renderer reloads, app restart, and interrupts expose a state machine problem:

  • completed/failed/interrupted turns can restore with markedStreaming=true;
  • event handlers can receive turn/started or item events for an unknown conversation;
  • turn/completed does not appear sufficient to repair a renderer that missed prior item events;
  • missing item IDs then produce Item not found in turn state.

Likely fixes:

  • Treat terminal backend state (completed, failed, interrupted) as authoritative on resume. A terminal turn should not rehydrate as streaming.
  • On resume, if the renderer missed item events, backfill/reconcile the turn item list before accepting live item delta events.
  • Buffer turn/started / item events for a short window while conversation state is being hydrated instead of immediately dropping them as unknown.
  • Include item summaries or item ids on turn/completed, or add a cheap thread/turns/items/list reconciliation path for active/recent turns.
  • Add a renderer reload / app restart integration test where a turn is mid-retry, mid-tool, and immediately post-completion.

Why supports_websockets = false is only a partial workaround

It can avoid the WebSocket-close / 75s startup / 300s stale-WebSocket parts of the problem, and it is useful for users with bad WS routes. But it does not fully cover:

  • HTTP/SSE pre-first-output stalls reported by @zcyyyds-test;
  • post-tool continuation stalls that may happen after HTTP fallback;
  • renderer state bugs like markedStreaming=true, unknown conversation, and missing turn items;
  • subagent/background-agent failures reported in #24475.

So "force HTTP/SSE" is a practical mitigation, not a complete RCA.

Practical user workarounds while this is being fixed

  • If every new/resumed turn starts with Reconnecting... 2/5..5/5, try forcing HTTPS/SSE via a provider override with WebSockets disabled, where available. A first-class config flag would be much better.
  • Run codex doctor --all and test IPv4 vs IPv6 reachability if WebSocket connect timeouts are consistent. #23434 suggests IPv6 route preference can matter.
  • If Desktop shows a completed/failed/interrupted turn as still streaming after restart, assume renderer state may be stale. A full Desktop restart may rehydrate, but it is not a reliable fix if the underlying turn state is corrupted.
  • For public reports, screenshots of the exact Reconnecting... N/5 state plus sanitized local log snippets are useful. The UI error text may not survive in durable Desktop logs.
  • Do not rely on performance trace upload as the only artifact. If trace upload fails and deletes the local artifact, the best reproduction evidence is lost.

Minimal maintainer test matrix

I think one focused integration matrix would isolate most of this:

  1. default WebSocket, fresh tiny prompt;
  2. default WebSocket, resumed old thread;
  3. forced HTTP/SSE, fresh tiny prompt;
  4. forced HTTP/SSE, long context;
  5. post-tool continuation after several quick tool calls;
  6. reused WebSocket after idle;
  7. server close before response.completed;
  8. stream opens but no first event for >60s;
  9. initial request sent but no response headers/stream object;
  10. renderer reload/app restart while a turn is retrying;
  11. renderer reload/app restart immediately after turn/completed;
  12. interrupt/stop during retry and then submit a follow-up;
  13. subagent/background-agent dispatch;
  14. image-generation rejection path.

For each case, assert:

  • user-visible state label is phase-specific;
  • durable logs include request attempt id, transport, retry count, fallback, first-event timing, and terminal state;
  • no completed/failed/interrupted turn restores as markedStreaming=true;
  • no item event is dropped permanently because the renderer had not hydrated the conversation yet;
  • Stop leaves a terminal or explicitly detached/unknown state, not a pseudo-streaming state.

Short version

The exact WebSocket error reports are real and source-explained. The long 5-minute and 75-second variants are also source-explained by current defaults. The 10-30 minute no-output cases and the post-restart phantom/stale UI cases require additional explanation: either backend/request/first-event stalls on HTTP/SSE too, plus Desktop state recovery bugs that keep terminal turns visually alive or lose item state.

The durable fix is probably not one timeout tweak. It is:

  • phase-specific stream/request telemetry;
  • first-event and initial-response watchdogs;
  • safer idempotent resume/retry semantics;
  • a first-class HTTP/SSE fallback option;
  • and renderer/session reconciliation that treats terminal backend turn state as authoritative.
w2jmoe · 1 month ago

Interesting report 👀

The part that stands out is that the session eventually resumed normally after a ~30 minute silent gap.

That feels less like a pure model-generation slowdown, and more like a “turn orchestration / runtime state” issue somewhere between:

  • reasoning allocation
  • transport reconnect
  • queue scheduling
  • tool/session state hydration

Especially because there were no persisted reasoning/tool events during the stall window.

In mixed-runtime agent systems we've been seeing a similar pattern:
sometimes the expensive part is not inference itself, but the coordination layer waiting on internal state transitions that never fully fail, yet also never fully advance 😅

The “alive but silent” state is particularly nasty for observability because from the user side it looks identical to:

  • backend congestion
  • websocket reconnect loops
  • context compaction stalls
  • scheduler deadlocks
  • deferred reasoning allocation

Your local timing analysis is actually super valuable here ✨
The p95 vs 30m outlier comparison helps separate “normally slow xhigh” from “something entered a pathological runtime state”.

Really good investigation.

OverHash · 1 month ago
Interesting report 👀 The part that stands out is that the session eventually resumed normally after a ~30 minute silent gap.

Your comment is a lot of yap and presumably AI slop, but I experienced a HTTP 504 returned (and it looked to be by Cloudflare, rather than OpenAI servers) after precisely 30 minutes of an HTTP call that had no response (i.e., 30 minutes until TTFB response). I presume this is what OP experienced.

Sometimes OpenAI servers have responded in less time, one time it was 22 minutes for me.

Nikit0s36 · 1 month ago

Session: 019e6935-846f-7402-a8ad-7973d2e54db2

maxclud4-tech · 1 month ago

ничего не прмогает подписка идет компесации нет!!! байкот и переход на антропик

Avishayy · 1 month ago

Strong +1 to @MisterRound's bucket #2 ("stale or reused WebSocket waits too long for first event") — we hit exactly this on codex-cli 0.133.0 (app-server mode, gpt-5.5, responses_websocket transport, ChatGPT-subscription auth), and have a packet capture that confirms the mechanism on the wire, not just from source.

When a turn stalled, the app-server emitted turn/started and then no events. The pcap shows codex pushed the request onto a pooled connection the server had already closed, and got an immediate RST:

T+0.203  CLIENT.36670 > chatgpt:443  [P.]   # request pushed onto a reused pooled conn
T+0.245  chatgpt:443 > CLIENT.36670  [R]    # server RSTs it, ~42ms later
#  the other pooled connections then exchange only keepalive ACKs — no data —
#  and no fresh connection is opened for the rest of the stall window

So the connection wasn't slow or the backend silent — the server actively reset a reused idle connection, and codex did not fast-fail on the RST. It kept waiting for a stream event that could never arrive. On runs we let proceed without intervention, recovery came at the ~300s mark, and the codex_core::client span closed with:

model_client.stream_responses_websocket{... transport="responses_websocket"}:
  codex_core::client: close time.busy=3.74ms time.idle=304s

time.idle=304s lines up exactly with the DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300_000 you found — i.e. a RST on a reused connection isn't surfaced as an immediate connection error; it
falls through to the full 300s idle timeout before the retry fires. That's direct evidence for your suggested fixes: verify liveness / avoid reuse after idle, and split a short first_event_timeout from the 300s stream_idle_timeout (or treat a TCP RST as a fast retry trigger).

gmnikhil · 1 month ago

is it possible to surface this to user? I'm noticing stuck behaviour and i'm not sure if its actually working or rate limited or stuck due to some connectivity issue. why does it need to say thinking all the time? Sometimes i've noticed its stuck on other things also, like editing, reading and in between.

MisterRound · 1 month ago

I'm not seeing this particular issue anymore, at least not for the past maybe week or so

bprovencher-ai · 1 month ago

I am having his issue right now, not able to return a single prompt, really frustrating

hellocrazyinc-hue · 1 month ago

Oh, I can assure you, it is. I have experienced it many times recently including 3x today. Extremely frustrating and wild this is not fixed (I am on latest Codex).

ai-hustle-bro · 1 month ago

For the recent "not sure if it is working, rate-limited, or stuck" comments, I put together a small local-only PowerShell diagnostic that may make future reports easier to classify:

https://gist.github.com/ai-hustle-bro/6db680e4fd932edc580790ed1eba69d6

It scans local Codex session JSONL and log files for two timing-only signals:

  • user-message to first assistant/reasoning/tool item gaps
  • model_client.stream_responses_* spans where time.idle is high

It does not upload anything, and by default it does not print prompts, responses, or exact local file paths. It replaces paths with short SHA-256 hashes unless -IncludeFileNames is explicitly passed.

This does not claim a root cause or fix. It is just a privacy-preserving report generator so a "Thinking"/"Working" stall can be described as, for example, "first output gap was 984s on gpt-5.5/xhigh" or "stream idle span was 305s on responses_http".

gmnikhil · 1 month ago

I asked codex to convert this to be able run via python.
i dont know whats happening, but whenever i face the stuck issue in codex now, i run this script.
This script runs and then automatically the agent is able to continue.

i'll share some output:

# Codex First-Output Gap Report

- Generated UTC: 2026-06-05T11:31:59.098021Z
- Codex root: hidden; use --include-file-names to print paths
- First-output threshold: 10s
- Stream idle threshold: 120s
- Privacy: prompt and response text are not printed.

## First-Output Gaps

| Gap seconds | User UTC | First output UTC | Model | Effort | Source |
|---:|---|---|---|---|---|
| 199.7 | 2026-05-22T05:30:59.712000Z | 2026-05-22T05:34:19.396000Z | gpt-5.4-mini | medium | sha256:1da290fdf533 |
| 110.9 | 2026-05-21T18:24:29.867000Z | 2026-05-21T18:26:20.805000Z | gpt-5.4-mini | medium | sha256:1ca53da5fda7 |
| 81.3 | 2026-05-27T08:16:44.474000Z | 2026-05-27T08:18:05.802000Z | gpt-5.4 | medium | sha256:587dcf3f745b |
| 63.9 | 2026-06-05T05:31:24.065000Z | 2026-06-05T05:32:28.005000Z | gpt-5.4 | high | sha256:240825a81e4b |
| 63.6 | 2026-06-03T08:47:48.174000Z | 2026-06-03T08:48:51.743000Z | gpt-5.4 | medium | sha256:6e4702cf0074 |
| 60.7 | 2026-06-04T12:14:44.923000Z | 2026-06-04T12:15:45.587000Z | gpt-5.5 | high | sha256:f6d780fe6616 |
| 56.7 | 2026-06-04T10:34:55.425000Z | 2026-06-04T10:35:52.151000Z | gpt-5.4 | low | sha256:eee376938aa4 |
| 46.4 | 2026-06-03T11:05:19.290000Z | 2026-06-03T11:06:05.670000Z | gpt-5.4 | medium | sha256:d8380cb27aa8 |
| 44.7 | 2026-05-21T12:14:07.976000Z | 2026-05-21T12:14:52.716000Z | gpt-5.4 | medium | sha256:aa6006f177f0 |
| 43.5 | 2026-06-02T07:29:31.907000Z | 2026-06-02T07:30:15.419000Z | gpt-5.4-mini | high | sha256:3a63c3ace061 |
| 39.0 | 2026-06-05T10:13:15.928000Z | 2026-06-05T10:13:54.893000Z | gpt-5.5 | high | sha256:69933f14b9a6 |
| 35.8 | 2026-05-28T18:10:41.942000Z | 2026-05-28T18:11:17.780000Z | gpt-5.4 | medium | sha256:a7f68aaec1da |
| 35.5 | 2026-05-21T15:21:21.080000Z | 2026-05-21T15:21:56.591000Z | gpt-5.3-codex | medium | sha256:1ca53da5fda7 |
| 33.6 | 2026-06-03T09:54:47.081000Z | 2026-06-03T09:55:20.660000Z | gpt-5.4 | medium | sha256:d8380cb27aa8 |
... more here (i've redacted to keep it short)


## Stream Idle Spans

No stream idle spans met the threshold.

Also note, whenever i faced this, i used to stop the session. sometimes sending some newer messges worked. sometimes it didnt work.

PS: I'm on mac. just noticed this is related to windows. sorry if this is the wrong place.

ai-hustle-bro · 1 month ago

I added a Python version to the same diagnostic gist:

https://gist.github.com/ai-hustle-bro/6db680e4fd932edc580790ed1eba69d6

New file: codex_first_output_gap_report.py

Usage:

python codex_first_output_gap_report.py \
  --codex-root ~/.codex \
  --output codex_gap_report.md \
  --min-first-output-seconds 120 \
  --min-idle-seconds 120

It keeps the same privacy defaults as the PowerShell version: no prompt text,
no response text, and local file paths are replaced by short hashes unless
--include-file-names is explicitly passed.

One caveat on the "running this makes the stuck agent continue" observation:
the script is read-only, so I would not treat it as a fix. If it correlates
reliably, the interesting data point is probably the timestamp: whether the
agent continues exactly when the stuck stream times out, when a log span closes,
or when another local process reads the files. The report output plus the Codex
app/CLI version should still be useful evidence.

gmnikhil · 1 month ago

Wanted to give an update:
Faced this only once since the last message. That time running the script as per my prev msg didnt fix.
Haven't faced it since. (been over a week)

gmnikhil · 1 month ago

faced this today @ai-hustle-bro :

# Codex First-Output Gap Report

- Generated UTC: 2026-06-17T06:00:07.061181Z
- Codex root: hidden; use --include-file-names to print paths
- First-output threshold: 120s
- Stream idle threshold: 120s
- Privacy: prompt and response text are not printed.

## First-Output Gaps

| Gap seconds | User UTC | First output UTC | Model | Effort | Source |
|---:|---|---|---|---|---|
| 160.7 | 2026-06-17T05:54:56.778000Z | 2026-06-17T05:57:37.442000Z | gpt-5.4 | medium | sha256:d418e434ee88 |

## Stream Idle Spans

No stream idle spans met the threshold.

## Phase Hints

These are conservative hints for triage, not root-cause claims.

- Worst persisted user-to-first-output gap: 160.7s on model 'gpt-5.4' effort 'medium'.

This is the new python script output u shared.
The conversation was started an hour back and it concluded correctly.
later after some 30+ min i sent a message and it was stuck for a while.

i started a new conv and typed "hi". it worked. the other conversation remained stuck.
after sometime, agent started responding.
at all times, it showed "thinking"

leo2783 · 20 days ago

I’m seeing a similar stall, but the duration was at least over 1 hour.

Environment/context:

  • Surface: Codex extension for VS Code
  • Model: GPT-5.5
  • Reasoning: high
  • Mode: Goal mode enabled
  • Timing: it happened after Codex had already modified files

Observed behavior:
After Codex made file changes, the session stayed stuck without visible progress for more than an hour. Using “Resume goal” did not recover the run. The only workaround that helped was to interrupt the entire session first, then use “Resume goal” afterward.

From the user side, this looks similar to the long thinking / pre-output stall described in this issue, except in my case it happened in the VS Code extension with Goal mode enabled rather than the Desktop app.