Repository navigation
[Bug] Codex hangs in Prime Agent: evidence and automatic recovery #3128
Replies: 3 comments
|
Investigated this report and produced a candidate fix, validated on a fork of this repository. Root cause, fix and validation from the working notes:
Diff: awhite0030/prime-agent@main...fix-codex-stream-inactivity-timeout-7664453201931427247 The change passes |
|
@kevinjosethomas, could you take a look or route this to the maintainer responsible for Codex transport? I saw your provider resilience work in #2761 and the Rust migration in #2524. The evidence and tested fork above concern the earlier TypeScript implementation. Is this inactivity recovery gap already covered in Rust, or should the reproduction and proposed timeout be adapted to the current implementation? |
|
hey! thanks for the careful measurements and the direct Rust follow-up :) To that question: I haven't verified equivalent event-reset inactivity recovery across both Rust transports, so I'm not calling this fixed. I'm retiring the old TypeScript incident after the rewrite. Please retry v0.10.0 and share the transport, last-event timing, and recovery behavior if it hangs; adapting the reproduction would help establish the current gap before choosing a timeout policy. |
Uh oh!
There was an error while loading. Please reload this page.
After recurring Codex hangs in Prime Agent, including one lasting 12h31m before cancellation, I added an inactivity timeout in a fork. In a live run, it detected five minutes without response events, retried automatically, and completed the turn without intervention.
The implementation and test details are in my fork’s PR #1.
What I observed
The original hangs occurred with Prime Agent 0.9.7, macOS ARM64,
openai-codex,gpt-6-astra, reasoningmax, and automatic transport selection.Across 74 requests from the main chat and its children, eight main requests were interrupted after 3m43s to 12h31m. Successful main requests had a median duration of 21 seconds and a maximum of 2m37s.
Four cancellations followed by “continue” produced:
Each retry’s logged request body size increased by 68 bytes, consistent with “continue”. This weakens a fixed context limit explanation, but cancellation also changes connection state and starts a new generation.
Snapshots showed a responsive daemon,
isStreaming=true, zero retries, no tool execution or compaction, context below the limit, and unchanged partial reasoning. Most affected requests reached the first response milestone within seconds. The logs do not establish uninterrupted silence throughout those waits; “Thinking” does not establish ongoing model computation.The installed client can wait indefinitely on an open stream. Its WebSocket cleanup timer expires idle cached connections after five minutes; active responses need an error, closure, or terminal event to leave the wait. The original backend or transport cause remains unknown. No parser deadlock, shared connection bug, or quota error was demonstrated.
Proposed change
The fork adds a configurable inactivity timeout of five minutes for Codex WebSocket and SSE:
response.*events reset the deadline; transport keepalives do not.CodexStreamTimeoutErrorwithETIMEDOUTthrough existing retry handling, discards failed connections and continuation state, and makes subsequent attempts in the session use SSE after a WebSocket failure.retry.provider.timeoutMs, with an explicit timeout on an individual request taking precedence.This limits inactivity per transport attempt. Responses can run longer while events arrive, and fallback, retries, and extended recovery can increase the total wait.
Live recovery
I tested commit
3153c65cin an isolated daemon with copied chat history. That build reports version 0.9.6; the original installed binary was 0.9.7.On September 29, 2026, the resumed chat recorded these events (Warsaw, UTC+2):
response.reasoning_summary_text.deltaNo manual cancellation or new user message occurred between timeout and retry. The successful retry lasted 5m34s, demonstrating that the timeout allows longer responses with progress. A separate review chat and two children completed 60 requests without timeouts. These observations do not establish how often the limit might interrupt legitimate silent reasoning.
Automated validation
The 26 new regression cases exercise the real Codex provider, AgentSession, and SDK with deterministic simulated transports. They cover both transports, recovery, deadline resets, cancellation during requests and backoff, cached cleanup and manual resume, retry exhaustion and extended recovery limits, configuration forwarding, and request overrides. They also check that retries exclude failed partial output and do not duplicate the recovered tool call or final answer.
All 195 focused tests, shuffled runs, and repository checks passed. Disabling timeout, deadline reset, cleanup, or configuration forwarding makes the corresponding regressions fail. These tests validate client recovery under controlled failures.
Related reports and feedback
#1232 reports the same symptom; PR #1362 proposed an SSE timeout. Both closed during the move to Discussions, which did not establish a fix. Discussion #1405 covers unbounded waits and quota handling. This adds measured WebSocket inactivity and automatic recovery, with a proposal covering both transports.
My September 14 Codex desktop report also records WebSocket idle timeouts before and after successful compaction. Codex detected inactivity and retried, whereas unpatched Prime Agent kept waiting. This may help investigate a shared service or transport problem, but a common cause has not been established.
Would maintainers welcome a focused upstream PR for this behavior? Five minutes may still be too long to wait before recovery. Would a shorter inactivity limit be a better default while still accommodating legitimate silent reasoning? I’d welcome feedback on that tradeoff and any transport changes or recovery requirements this should align with.
All reactions