Codex stale-thinking spinner persists after stream state desync (thread stale detection + targeted recovery results)
Resolved 💬 12 comments Opened Feb 26, 2026 by g0udurix Closed Feb 27, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor)
responded on this thread — see the highlighted reply below.
Reproducibility summary
- Date: 2026-02-26 (UTC)
- Scope: local sessions under
~/.codex/sessions,~/.codex/archived_sessions, app logs under~/Library/Logs/com.openai.codex. - Detector is read-only for discovery; recovery attempts are targeted per-thread only.
- This issue is tracked with generated artifacts under:
/Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T221328Z/- No raw conversation text or secrets are included in the artifacts.
Key stale indicators
019c9a7f-46f2-7e03-aceb-89afe89a581b=> stale score=12.6, unmatched_starts=2, heartbeat_ms=4650543, in_progress_stream=true, marked_streaming=true, turn_not_found_count=1272.019c489a-0eb0-7142-8c30-e92ced2cf74a=> at-risk score=5.8, unmatched_starts=0, heartbeat_ms=8332044, in_progress_stream=true, marked_streaming=true, turn_not_found_count=0.
Recovery results from tooling
- Targeted conservative recovery (
--recover) was executed per-thread after detection on the two above threads. - For both threads, the app-server returned RPC error:
thread not foundduringturn/interrupt. - Recovery status was tracked as
thread_not_found; operator guidance is to continue in a fresh thread using a compact handoff of last confirmed artifacts.
Observable loop behavior
- Thread UI remains in thinking/streaming indications after
maybe_resume_successappears withlatestTurnStatus=inProgressandmarkedStreaming=true. Turn not found for eventandthread-stream-state-changed/ handler warning patterns continue in logs around those thread IDs.- This suggests a stream-state reconciliation or state-database migration/cleanup race rather than pure compute inactivity.
Ask
- Please investigate stream-state lifecycle for
thread-stream-state-changed,maybe_resume_success, andTurn not found for eventreconciliation. - Confirm whether stale stream markers should be auto-cleared when referenced turns are no longer resolvable.
12 Comments
Follow-up from production-hardening run:
/Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T221428Z/019c9a7f-46f2-7e03-aceb-89afe89a581b019c489a-0eb0-7142-8c30-e92ced2cf74astaleandat-riskrespectively (heartbeat > 1h, stream markers present)thread_not_foundfor both IDs onturn/interrupt.thread_not_foundstate and emits operator fallback guidance rather than generic failure.Artifacts in this run include:
stale_threads.jsonlactions.jsonincident_summary.mdredacted_session_map.csvopenai_feedback_draft.mdrecovery_results.jsonPotential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
you are stuck somewhere. stop and replan/Reprise: j’ai relancé un test MLX), and explicit per-thread recovery forcing for--recover-threadtargets./Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T221609Z/019c9ba7-e168-78a1-97f2-a188bc74d44aalso now surfaces as targeted recovery candidate (with phrase-signal evidence), but app-server returnsthread_not_foundwhen interrupted.Follow-up production-hardening result (run ):
[stale-think] actions=/Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T222114Z/actions.json
[stale-think] summary=/Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T222114Z/incident_summary.md
[stale-think] map=/Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T222114Z/redacted_session_map.csv
[stale-think] openai_feedback=/Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T222114Z/openai_feedback_draft.md
[stale-think] github_payload=/Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T222114Z/github_issue_payload.json
[stale-think] stale_count=2 total_threads=57
Follow-up production-hardening result (run 20260226T222100Z):
Updated follow-up packet (run 20260226T222239Z):
019c9a7f-46f2-7e03-aceb-89afe89a581bandat-risk019c489a-0eb0-7142-8c30-e92ced2cf74ain this execution window./Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T222239Z/final_rc_report.md.codex_cli 0.101.0vscodex_app 0.105.0(version_alignment: mismatch).Recovered thread: resume?ornew(fresh thread handoff). Automation prompt updated accordingly inautomations/codex-stale-think-watch/automation.toml.Follow-up production-hardening run after CLI/App alignment:\n\n- Aligned CLI to app protocol version via
changed 2 packages in 435ms (now both report ).\n- Re-ran targeted recovery on: and .\n- Result remains terminal for both: app-server returns on ; status in .\n- Operator handoff remains explicit: if thread still visible in UI, otherwise to continue from compact handoff.\n- Artifacts for this run:\n - stale_threads.jsonl\n - actions.json\n - openai_feedback_draft.md\n - recovery_results.json\n - incident_summary.md\n - github_issue_payload.json\n\nCurrent blocker for closure is persisted stream-state artifact for these IDs, likely orphaned run IDs/state-db mismatch independent of protocol version skew.
Follow-up production-hardening run after CLI/App alignment:
npm install -g @openai/codex@0.105.0(now both reportcodex-cli 0.105.0).thread not foundonturn/interrupt; statusthread_not_foundin/Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T230417Z/recovery_results.json.Recovered thread: resume?if thread still visible in UI, otherwisenewfor compact handoff in a fresh thread.Current blocker for closure is persisted stream-state artifacts for these IDs, likely orphaned run IDs/state-db mismatch independent of protocol version skew.
RC-ready summary posted in local packet:
/Users/jeremie/.codex/reports/codex-stale-thinking/2026-02-26/20260226T230417Z/final_rc_report.mdThis confirms both flagged threads are in final state:
thread_not_foundon targeted recovery attemptsRecovered thread: resume?ornewCLI/App are now aligned at
0.105.0(npm list -g @openai/codex --depth=0), so remaining blocker is stale/orphaned thread state in old session artifacts.Follow-up (2026-02-27, production rollout + evidence):
We implemented and validated an automated stale-think containment path in local production.
What is now automated:
1) Detect stale threads from logs/transcripts using:
2) Conservative per-thread recovery first (no global restart).
3) If recovery fails (
thread_not_found/ still stale), auto-relaunch to a fresh thread in Plan mode with continuity context and explicit ops guidance bootstrap.4) Crash protection in the same loop:
Operational evidence (sanitized):
019c489a-0eb0-7142-8c30-e92ced2cf74a019c9a7f-46f2-7e03-aceb-89afe89a581bthread_not_foundeven while stale indicators remain in historical telemetry.019c9fee-0556-7173-9509-d6f3b64eaef8(turn completed)019c9fee-0555-77f0-b351-9ba90c013fd0(turn entered inProgress then continued)Updated hypothesis:
thread not found) while UI/log-derived stale state still persists for that thread lineage.Requested backend/UI focus:
1) hard reconciliation when source thread is not resolvable but UI still marks streaming.
2) idempotent closure for orphaned inProgress turn markers.
3) stronger handling for
thread-stream-state-changed+ missing turn references (Turn not found for event).Sanitized artifact roots used for this validation:
~/.codex/reports/codex-stale-thinking/2026-02-27/~/.codex/runtime/relaunched_threads.jsonlIf useful, I can post a compact event timeline matrix keyed by
(thread_id, timestamp, event_type)without prompt contents.I presume this is a bot. Closing.
Additional follow-up (same day):
New high-signal anomaly observed while hardening fallback logic:
thread/readsucceeds for affected thread IDs, butturn/interrupton the same(threadId, turnId)returnsthread not found(-32600).Example behavior class (sanitized):
1)
thread/read(threadId=X, includeTurns=true)=> returns turns incl.inProgress.2)
turn/interrupt(threadId=X, turnId=Y)=>thread not found: X.3) UI can remain in spinner state despite inability to interrupt.
Operational mitigation added:
inProgress, auto-archive that relaunch thread.Result from latest validation window:
This seems like a request-routing/state-index inconsistency between read-path and interrupt-path for thread identity resolution.