Codex TUI sometimes goes fully idle for 30-90+s after a turn completes (all threads parked, not network/lock related) — reproduces on 0.139.0 and 0.142.5
Summary
After a turn's response fully completes (SSE response.completed received, model output rendered), the codex TUI process sometimes goes completely idle — 30+ seconds with no further activity of any kind, before either eventually recovering or requiring an external kill. A live sample-based thread dump taken mid-stall shows every thread (main, codex-main, all tokio-rt-workers, sqlx-sqlite-workers, OpenTelemetry) cleanly parked in a condvar/semaphore wait — i.e. this is not a deadlock, livelock, or busy loop. It looks like whatever event/wakeup is supposed to advance the session back to an interactive-ready state after a turn completes is sometimes never delivered.
Environment
codex --version: reproduced on both 0.139.0 and 0.142.5 (upgraded specifically to test whether this was already fixed — it was not)- OS: macOS 15.5 (24F74), Darwin 24.5.0, arm64 (Apple Silicon)
- Invocation:
codex --dangerously-bypass-approvals-and-sandbox -c model_reasoning_effort=high, driven programmatically via a real pty (node-pty), notcodex exec/headless mode - Model: gpt-5.5,
model_reasoning_effort=high
Repro steps
- Launch
codexin a real pty (not headless) in an empty scratch directory with no project files. - Submit a trivial prompt (e.g. "reply with exactly: hello").
- Once idle without having produced the expected reply, send a follow-up nudge instructing it to actually run a build/tests and show real output (a generic "you're not done yet, do X" instruction — no special characters, no slash-commands).
- Observe: the model responds correctly and quickly (confirmed via server logs, see below) — but the CLI process itself then produces zero further observable activity (no CPU usage, no new log lines, no TUI update) for 30–90+ seconds, sometimes longer, before I gave up and killed it.
This reproduced on 3 out of 3 attempts across both 0.139.0 and 0.142.5.
Evidence
~/.codex/logs_2.sqlite (the app's own structured log DB) shows the model's response actually completing quickly and correctly. Example from one repro (0.139.0):
06:14:54 - continuation nudge submitted
06:15:55.178 - event.name="codex.sse_event" event.kind=response.completed (input_token_count=47060, output_token_count=384, reasoning_token_count=32)
06:15:55.178 - response.output_text.done: model correctly ran `npm run build`/`npm test` in the scratch dir, reported the resulting ENOENT (no package.json) and explained there was nothing to build
06:15:55.557 - analytics-events POST completes (200 OK)
06:16:30.452 - (35s later) next log line is an unrelated OTel metrics export — nothing turn-related in between
(process was still producing nothing when I killed it ~17s after that)
A second repro on 0.142.5 (with the memories feature explicitly disabled via ~/.codex/config.toml to rule it out as a red herring — it was not the cause, the same stall recurred with zero codex_memories_write log lines present) shows the identical shape:
10:56:56 - continuation nudge submitted
10:57:24.291 - response.completed
10:57:24.813 - analytics-events POST completes (200 OK)
10:57:43.984 - (19s later) next log line is again just an OTel metrics export
(CPU stayed at 0.0% through at least t+120s from nudge, i.e. 90+s of total silence after the response had already completed)
A live thread sample (sample <pid> 3) taken during the stall on the 0.142.5 repro shows all threads idle:
Thread_...: DispatchQueue_1 (main) -> __ulock_wait (pthread_join)
Thread_...: codex-main -> parking_lot::condvar::Condvar::wait_until_internal -> _pthread_cond_wait
Thread_...: tokio-rt-worker (x7) -> parking_lot::condvar::Condvar::wait_until_internal -> _pthread_cond_wait
Thread_...: reqwest-internal-sync-runtime
Thread_...: OpenTelemetry.Metrics.PeriodicReader -> semaphore_timedwait_trap
Thread_...: sqlx-sqlite-worker-0..8 -> semaphore_wait_trap (dispatch semaphore wait)
Thread_...: notify-rs fsevents loop
No thread is spinning, blocked on I/O, or waiting on a contended lock — every thread believes there is genuinely nothing to do. This points to a missed wakeup / dropped continuation somewhere in the turn-completion -> return-to-interactive-prompt path, rather than a resource contention or network issue.
What I ruled out
- Network/API latency: the model's response is present and correct in the logs well before the stall window begins; this isn't the server being slow.
- The
memoriesfeature: disablinggenerate_memories/use_memoriesinconfig.tomldid not change the behavior; acodex_memories_write::phase1log line that appeared coincidentally near the stall in one 0.142.5 repro turned out to be unrelated (it doesn't appear in the 0.139.0 repro's log at all, and disabling the feature entirely didn't stop the stall). - Deadlock/lock contention: ruled out via the thread sample above — everything is idle, nothing is blocked waiting on a held lock.
- Upgrading 0.139.0 -> 0.142.5: does not fix it; identical behavior on both versions.
Expected behavior
After a turn's response completes and is rendered, the CLI should promptly return to an interactive-ready state (idle prompt, accepting new input) — not sit with all threads parked for 30–90+ seconds with no observable progress.
Additional context
This was found while building an external automation harness that drives codex through a real pty (to exercise its normal interactive mode rather than -p/exec headless mode). The harness has its own timeout/retry logic that absorbs this gracefully, so it's not blocking on our end — reporting mainly because the stall (a) reproduced consistently across CLI versions and (b) the evidence above narrows it down further than "it just feels slow sometimes."
Happy to provide the full sample output, the raw logs_2.sqlite rows, or reproduce with more logging if useful.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗