Codex Desktop: thread/read fails on empty rollout after closing browser sidebars
What issue are you seeing?
Codex Desktop on Windows showed a JavaScript/UI error after all browser/sidebar panes were closed. The desktop logs show the browser sidebar window being removed, and a later app-server/UI path shows thread/read failing because a precomputed rollout file existed but was still empty. After that failure, Electron continued receiving events for a conversation that the UI had not loaded, producing repeated unknown conversation errors.
Observed environment:
- Codex Windows Store package:
OpenAI.Codex_26.527.3686.0_x64__2p2nqsd0c76g0 - App log release field:
26.527.31326 - OS: Windows 10.0.26200
- Locale/timezone:
zh-CN,Asia/Shanghai
Relevant sanitized log excerpts:
2026-06-01T06:48:43.849Z info [browser-sidebar-manager] IAB_LIFECYCLE syncing browser use active state conversationId=<redacted> isActive=false ownerWebContentsId=1 turnId=null windowId=1
2026-06-01T06:48:43.850Z info [browser-sidebar-manager] IAB_LIFECYCLE syncing browser use active state conversationId=<redacted> isActive=false ownerWebContentsId=1 turnId=null windowId=1
2026-06-01T06:48:43.923Z info [browser-session-registry] IAB_LIFECYCLE closed browser sidebar page conversationId=<redacted> windowId=1
2026-06-01T06:48:43.927Z info [browser-session-registry] IAB_LIFECYCLE closed browser sidebar page conversationId=<redacted> windowId=1
2026-06-01T06:48:43.927Z info [browser-session-registry] IAB_LIFECYCLE removed browser session window windowId=1
2026-06-01T13:12:00.856Z info [electron-message-handler] IAB_LIFECYCLE webview captured browser use session route conversationId=<redacted> disposeAfterSessionActivity=true reason=hydrated_collab_thread
2026-06-01T13:12:00.927Z info [AppServerConnection] response_routed broadcastFallback=false conversationId=<redacted> durationMs=69 errorCode=-32603 hadInternalHandler=false hadPending=true method=thread/read originWebcontentsId=1 targetDestroyed=false
2026-06-01T13:12:00.929Z error [electron-message-handler] Request failed conversationId=<redacted> durationMs=72 error={"code":-32603,"message":"failed to read thread: thread-store internal error: failed to read thread C:\\Users\\<user>\\.codex\\sessions\\2026\\06\\01\\rollout-2026-06-01T21-12-00-<redacted>.jsonl: rollout at C:\\Users\\<user>\\.codex\\sessions\\2026\\06\\01\\rollout-2026-06-01T21-12-00-<redacted>.jsonl is empty"} method=thread/read
2026-06-01T13:12:06.209Z error [electron-message-handler] Received turn/started for unknown conversation conversationId=<redacted>
2026-06-01T13:12:09.095Z error [electron-message-handler] Received item/started for unknown conversation conversationId=<redacted>
2026-06-01T13:12:16.243Z error [electron-message-handler] Conversation state not found conversationId=<redacted>
What steps can reproduce the bug?
The desktop UI trigger is timing-sensitive, but this was observed after closing all browser/sidebar panes so the final browser session window is removed.
The app-server path is reproducible with:
- Start a new thread so the response includes a precomputed rollout path.
- Ensure the rollout path exists as a zero-byte file before the first user message materializes the rollout.
- Call
thread/readwithincludeTurns: falsewhile the thread is already loaded in memory.
Current behavior treats the empty rollout file as a hard -32603 internal error instead of falling back to the live loaded thread snapshot.
What is the expected behavior?
Closing the final browser/sidebar pane should not leave the desktop UI in an error state. For a loaded thread whose precomputed rollout path exists but is still empty, thread/read should treat that rollout as not yet materialized and return the live thread snapshot for metadata-only reads.
For includeTurns: true, the server should continue returning the existing user-facing "not materialized yet" invalid-request response before the first user message.
Additional information
I found no exact duplicate issue for empty rollout, thread/read, and unknown conversation. I prepared a small patch with a regression test and a fix in ThreadRequestProcessor that only treats empty rollout errors as "missing" when the corresponding thread is already loaded in memory; see the issue comment for the fork branch because GitHub rejected opening a PR from my account.
2 Comments
Update: I prepared a minimal patch branch, but GitHub rejected creating a PR against
openai/codexfrom my fork:The branch and commit are available here for maintainers:
The patch adds a regression test and updates
ThreadRequestProcessorso a loaded thread can service metadata-onlythread/readfrom the live snapshot when the precomputed rollout path exists but is still empty.I hit the same failure mode on a newer Windows Codex Desktop build, with a slightly different user-facing trigger: intermittent Desktop/phone connection trouble recovered after restarting the phone-side Codex app.
Environment:
OpenAI.Codex_26.623.9142.0_x6426.623.70822win322026-07-01 12:08:35 KST(2026-07-01T03:08:35Z)Sanitized evidence:
The same rollout file was not permanently empty:
2026-07-01 12:08:35 KST2026-07-01 12:11:11 KST296113bytes92Follow-on pattern for the same
conversationIdin this log:unknown conversation:257No turns for conversation:150Conversation state not found:13rollout at .* is empty:1This supports the race-condition interpretation in the issue:
thread/readobserves a newly-created rollout path before the first record is materialized, returns a fatal internal error, and the renderer/app-server path then fails to associate subsequent events with the conversation even though the rollout later becomes readable.