Codex Desktop macOS: side chat receives turns but is not persisted, then Fork fails with no rollout found
What version of the Codex App are you using?
Codex Desktop for macOS 26.506.31421 (2620)
What subscription do you have?
Pro
What platform is your computer?
- Darwin
25.3.0 - Apple Silicon /
arm64
What issue are you seeing?
Forking a side chat in Codex Desktop for macOS fails with a Failed to fork chat toast. After checking local metadata and app logs, this looks like another instance of the side-chat persistence mismatch described in #20944, but on a newer macOS app build.
Related issues:
- #20944
- #20977
I am filing this separately because the visible failure here is immediate thread/fork failure from a side chat, and I was able to confirm the same local-state mismatch without dumping any message content.
For the failing side-chat conversation id 019e1071-83e9-72e3-b7c6-6abb38f9c282:
- The app log first shows
Conversation state not foundfor that id. - Immediately after,
thread/inject_itemssucceeds for that same id. - Multiple later
turn/startcalls succeed for that same id. - The browser-use route is captured and later completed for that same id.
thread/unsubscribesucceeds for that same id.- Later,
thread/forkfails three times for that same id withno rollout found for thread id .... ~/.codex/state_5.sqlitehas no row inthreadsfor that id.~/.codex/state_5.sqlitehas no matching parent or child row inthread_spawn_edgesfor that id.~/.codex/sessions/2026/05/10has no rollout JSONL file matching that id.
So the side chat appears usable while it is active in the renderer, but it is never registered as a persisted thread and never gets a rollout file. Fork then calls thread/fork with that side-chat conversation id and fails because the app/server cannot find a rollout for it.
Steps to reproduce
I do not have a clean minimal repro yet, but this happened during normal Codex Desktop usage:
- Open an existing Codex Desktop conversation.
- Start a side chat from that conversation.
- Run work in the side chat.
- Use the Fork action from the side chat.
- The app shows
Failed to fork chat.
Expected behavior
Either:
- Side chats should be persisted before they can receive turns and before the Fork action is available, so
thread/forkhas a valid thread row and rollout path.
Or:
- If side chats are intentionally ephemeral/non-forkable, the UI should not expose Fork for them and should explain that they cannot be promoted to regular chats.
Local evidence
Log excerpts from ~/Library/Logs/com.openai.codex/2026/05/10/...log, with no message content included:
2026-05-10T05:52:15.417Z error [electron-message-handler] Conversation state not found conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282
2026-05-10T05:52:15.420Z error [electron-message-handler] Conversation state not found conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282
2026-05-10T05:52:15.422Z info [AppServerConnection] response_routed ... conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282 ... method=thread/inject_items ... errorCode=null
2026-05-10T05:52:29.569Z info [AppServerConnection] response_routed ... conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282 ... method=turn/start ... errorCode=null
2026-05-10T06:15:49.193Z info [AppServerConnection] response_routed ... conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282 ... method=turn/start ... errorCode=null
2026-05-10T06:40:13.546Z info [AppServerConnection] response_routed ... conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282 ... method=turn/start ... errorCode=null
2026-05-10T06:52:15.432Z info [AppServerConnection] response_routed ... conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282 ... method=thread/unsubscribe ... errorCode=null
2026-05-10T07:09:29.114Z info [AppServerConnection] response_routed ... conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282 ... method=thread/fork ... errorCode=-32600
2026-05-10T07:09:29.115Z error [electron-message-handler] Request failed conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282 ... error={"code":-32600,"message":"no rollout found for thread id 019e1071-83e9-72e3-b7c6-6abb38f9c282"} ... method=thread/fork
2026-05-10T07:09:33.839Z error [electron-message-handler] Request failed conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282 ... error={"code":-32600,"message":"no rollout found for thread id 019e1071-83e9-72e3-b7c6-6abb38f9c282"} ... method=thread/fork
2026-05-10T07:09:34.602Z error [electron-message-handler] Request failed conversationId=019e1071-83e9-72e3-b7c6-6abb38f9c282 ... error={"code":-32600,"message":"no rollout found for thread id 019e1071-83e9-72e3-b7c6-6abb38f9c282"} ... method=thread/fork
Metadata checks:
sqlite3 ~/.codex/state_5.sqlite "SELECT COUNT(*) FROM threads WHERE id='019e1071-83e9-72e3-b7c6-6abb38f9c282';"
# 0
sqlite3 ~/.codex/state_5.sqlite "SELECT parent_thread_id, child_thread_id, status FROM thread_spawn_edges WHERE child_thread_id='019e1071-83e9-72e3-b7c6-6abb38f9c282' OR parent_thread_id='019e1071-83e9-72e3-b7c6-6abb38f9c282';"
# no rows
find ~/.codex/sessions/2026/05/10 -maxdepth 1 -name '*019e1071-83e9-72e3-b7c6-6abb38f9c282*' -print
# no files
I also saw several nearby Received turn/started for unknown conversation log entries for other conversation ids during the same app session, which may be related, but I have not traced those ids deeply.
Additional information
I avoided attaching full logs, database dumps, or rollout files because they can include private project/thread context. The excerpts above are limited to method names, timestamps, ids, and error messages.
7 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Additional findings after digging through the app-server path:
/sidestarts fromside_fork_config()and setsfork_config.ephemeral = true, so the side chat is loaded inThreadManagerbut remains pathless and does not get a rollout file.thread/inject_itemsandturn/start, because those use the loaded thread, butthread/forkfails because it only tries to read persisted thread-store history.no rollout found for thread id ...error is consistent with an unmaterialized ephemeral source rather than a missing file lookup for a normal persisted thread.I tried a patch along this path: when
thread/forkcannot find a rollout for a requested source id and the source thread is currently loaded/pathless/ephemeral, fall back to the loaded thread's in-memory response history and materialize the new promoted fork normally unless the caller requestedephemeral. The patch also preserves source metadata so the promoted fork keepsforked_from_idbehavior aligned with normal persisted forks.Focused verification passed:
git diff --checkcargo test -p codex-app-server live_thread_fork_history_is_resumed_without_rollout_pathcargo test -p codex-app-server thread_fork_can_promote_loaded_ephemeral_thread_without_rolloutcargo clippy -p codex-app-server --testsFull
cargo test -p codex-app-serverwas also run. The newthread_forkcoverage passed, but this local run had 5 failures that look environment-specific: several tests failed because git commit signing went through 1Password in test-created repos, and one skills warning-count assertion differed from the locally installed skills set.I also noticed the current contributing guide says external code contributions are by invitation only, and recommends sharing analysis, reproduction details, root-cause hypotheses, and a high-level fix outline directly in the issue first. So rather than asking for an unsolicited PR review, here is the proposed fix information in that format.
What
thread/forkto recover when the requested source thread has no persisted rollout but is still loaded in the app-server.ThreadManagerinstance but cannot currently be promoted/forked because the fork path only reads persisted thread-store history.SessionMetaitem to the in-memory fork history so the materialized promoted fork can preserveforked_from_idbehavior like normal persisted forks.Why
thread/inject_itemsandturn/startfirst.thread/forkasks the thread store for a rollout and getsno rollout found for thread id ..../sideappears to create exactly this kind of source by usingside_fork_config()withfork_config.ephemeral = true, so the source can be live but pathless.How
read_stored_thread_for_resume(... include_history=true)fails withno rollout foundand no explicit rolloutpathwas supplied, look up the source id inThreadManager.rollout_path: None.ephemeral.Patch branch/commit for reference, not as a request for unsolicited review:
Verification run locally
git diff --checkcargo test -p codex-app-server live_thread_fork_history_is_resumed_without_rollout_pathcargo test -p codex-app-server thread_fork_can_promote_loaded_ephemeral_thread_without_rolloutcargo clippy -p codex-app-server --testsI also ran
cargo test -p codex-app-server; the newthread_forkcoverage passed, but this local full-package run had 5 apparent environment-specific failures: test-created git repos hit 1Password-backed commit-signing errors, and one skills warning-count assertion differed from the locally installed skills set.Windows metadata-only corroboration of the no-row/no-rollout pattern.
The same four side-chat IDs checked for #27716 were absent from all persisted stores after the observed App Server restart workflow: 0 matching threads rows, 0 session_index.jsonl entries, 0 sessions rollouts, and 0 archived_sessions rollouts for each ID. This supports the distinction between a live ephemeral thread that can receive turns and a normal persisted thread that thread/fork can resume.
The local Cockpit evidence is limited to request ID, model/account metadata, success or HTTP status, latency, token counts, and estimated cost. It contains no request or response body, and no private logs or databases are attached.
This Windows case supports the proposed app-server behavior: if a source is still loaded, pathless, and ephemeral, thread/fork should be able to promote it from in-memory response history before restart; normal persisted fork behavior should remain unchanged. See #27716 for the discoverability side of the problem.
Same here: when trying to fork threads, I often get a failure to fork which is really annoying as a side thread might have turned into important work and need to be "elevated" into main work.
I can confirm this issue is still live. It happens for me when forking side-chats in the codex app on MacOS. Forking to the same workspace or a new worktree gives me the same missing rollout related error.
When side chats or subagent forks fail with
no rollout found, it often stems from uncommitted rollout pointers or subagent parent/child lineage breaks in local session state.You can inspect subagent hierarchies and local session rollouts using Codex Rescue:
Or view agent session graphs:
It operates entirely locally and read-only without modifying session files. If you test it, the finding codes or graph summary would help identify the lineage drop.