Desktop (ChatGPT.app 26.709): fanout sub-agent threads never registered in renderer — events dropped as "unknown conversation", sessions appear stuck while backend completes
What version of Codex is running?
Codex desktop hosted in ChatGPT.app 26.707.62119 (build 5211), Codex runtime 26.709.11516, bundled codex-cli 0.144.2, macOS 26.0 (arm64)
What subscription do you have?
ChatGPT (logged-in desktop)
What issue are you seeing?
Sub-agent threads spawned by a multi-agent/fanout session are never registered in the desktop renderer. Every event they emit is dropped with Received turn/started|turn/completed|item/started for unknown conversation, so sessions appear permanently stuck ("thinking" spinner) while the backend actually completes all turns. This reproduces on the newest ChatGPT.app-hosted desktop build (26.709), i.e. it is still present after the "Codex joins the ChatGPT desktop app" release.
This looks like the same family as #24475 / #23292 / #31090 / #32502, but I can add database-level evidence that pins the mechanism.
Log fingerprint
~/Library/Logs/com.openai.codex/.../codex-desktop-*.log fills with (≈2,500 lines in one afternoon; each event logged twice — once per renderer window incl. avatarOverlay):
2026-07-13T09:18:40.740Z error [electron-message-handler] Received turn/started for unknown conversation conversationId=019f5ac5-56e4-72b0-8df4-b9e40a5d18e2 rendererWebContentsId=1 ...
2026-07-13T09:18:40.740Z error [electron-message-handler] Received turn/started for unknown conversation conversationId=019f5ac5-56e4-72b0-8df4-b9e40a5d18e2 rendererWebContentsId=2 rendererWindowAppearance=avatarOverlay ...
2026-07-13T09:19:26.896Z error [electron-message-handler] Conversation state not found conversationId=019f5ac5-768f-7280-a4d1-50bc0138ac45 ...
Key detail: the very first event of each such conversationId is already "unknown" — the renderer never knew these threads at any point (it's not a mid-stream desync).
Database evidence (why I'm sure these are fanout sub-agents)
Every "unknown" conversationId maps to a sub-agent thread in ~/.codex/state_5.sqlite:
SELECT source FROM threads WHERE id = '019f5a8d-d462-...';
-- {"subagent":{"thread_spawn":{"parent_thread_id":"019f4ac7-...","depth":1,
-- "agent_path":"/root/overall_session_audit","agent_nickname":"Meitner","agent_role":null}}}
SELECT parent_thread_id, child_thread_id, status FROM thread_spawn_edges
WHERE child_thread_id IN (…all "unknown" ids…);
-- all rows point to the SAME parent session, status=open
The parent is a long-running supervisor-style session (multi-agent fanout). Over ~22h it spawned 135 child threads (2–7/hour, thread_spawn_edges all status=open). Every single one of them is invisible to the renderer; parent turns complete fine in the backend (turn/started: 31, turn/completed: 16 in one log window) while the UI shows stuck spinners.
Repro
~/.codex/config.toml→[features] multi_agent = true,multi_agent_v2 = true,enable_fanout = true- In the desktop app, run a session that spawns sub-agents (fanout audit/research task).
- Watch the log: each spawned child's
turn/startedarrives asunknown conversation; the child never appears in the UI; the parent appears stuck. - Fully restarting the app re-syncs threads that already exist in the DB (previously orphaned children become visible), but every child spawned after the restart is orphaned again — within minutes new
unknown conversationspam starts (verified: restarted at 17:11, new spawns at 17:18 were orphaned again).
Expected
Renderer should register spawned sub-agent threads (receive/handle a thread/started for children carrying parent_thread_id) before — or upon — their first event, so their activity is visible and the parent doesn't look hung.
Notes
- Consistent with the analysis in #24475 (child events arrive before/without parent-side registration). PR #23575 ("async subagent startup for thread spawns") looked like the right direction but was closed unmerged.
- Single-agent sessions on the same machine are completely unaffected.
- Backend/network are healthy (all turns complete; results appear after an app restart).
4 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Not a duplicate of #32502, I believe — same root-cause family, different frontend surface: #32502 is the VS Code extension face, while this report is the desktop app (ChatGPT.app-hosted) renderer face, with DB-level evidence (
thread_spawn_edges/threads.sourceshowing the spawned children the renderer never registers) and the restart-reorphan observation (children spawned after a restart are orphaned again within minutes).Worth noting: #32738 (Windows, filed the same day) shows the same fingerprint — "subagent events are treated as unknown conversations" — so this is cross-platform confirmation of the unregistered-child-thread mechanism rather than a duplicate. Happy to consolidate into #24475 if maintainers prefer one canonical thread.
Additional macOS reproduction on a newer ChatGPT-hosted Desktop build:
26.810.52044(bundle6662)codex-cli 0.148.0-alpha.926.6.1, Apple SiliconThe same dual-renderer
unknown conversationfingerprint is still present. In the completed 2026-08-16 app log:The useful new detail is that this is not limited to visible fanout subagents. Many sequences are successful internal summary generations:
Other unknown IDs in the same log do map to invisible subagent threads, consistent with this issue. The summary-generation IDs are temporary internal conversations and are not registered as visible renderer conversations, yet their turn lifecycle is still broadcast to both the primary renderer and the hidden
avatarOverlayand logged as errors.This suggests the root problem is broader event routing/classification: internal conversations that are intentionally invisible are sent through a renderer path that assumes every conversation ID must already be registered.
I am not claiming these log lines alone explain general Desktop slowness. Their combined text volume is small. The actionable bug here is that expected successful internal work is broadcast to unrelated renderer surfaces and classified as an error, obscuring real desynchronization failures and duplicating every lifecycle event.
No application bundle, rollout, or state database was modified while collecting this evidence.
I reproduced this and traced the startup error to app-server child-thread notification ordering.
When a runtime-spawned subagent is created,
try_attach_thread_listenerregisters it withThreadWatchManager::upsert_threadbefore the client has receivedthread/started. That registration can emitthread/status/changed; after the listener attaches, childturn/*oritem/*events can also arrive. A prewarmed desktop renderer therefore receives child-scoped lifecycle events before it has created conversation state, producingReceived item/started for unknown conversation/Conversation state not found.I implemented and validated this repair in https://github.com/urs-muff/codex/commit/b9b486a5af4980c2d8f2138447a49c6dd7ac7b19:
upsert_thread_silently.thread/startedto initialized parent connections.thread/started.The focused regression test passes. In the full
codex-app-serverrun, the new test passed and 1266/1267 tests passed; the only failure was an unrelated environment-sensitive marketplace test that assumesopenai-api-curatedis the first marketplace even when a valid personal marketplace exists.just fmtalso passes after installing the documented formatter prerequisites.Since this repository documents that external pull requests are not accepted, I am leaving the exact commit and evidence here for the Codex team to apply internally.