Skip to content

Desktop never resumes threads that appear while the app is running (created by an external app-server client) - endless loading, resumeState=null, until app restart #30916

Description

@Quentin-BRG

What version of the Codex App are you using (From “About Codex” dialog)?

26.623.81905 (Released July 1, 2026; MSIX package version 26.623.11225.0)

What subscription do you have?

Pro

What platform is your computer?

Microsoft Windows NT 10.0.26200.0 x64

What issue are you seeing?

Revision note (2026-07-02): this issue body was revised after further testing. The original report claimed the behavior depended on threadSource / CLI version and that some threads never open even after a restart — that was wrong. See the revision comment below for what changed.

When a thread appears while Codex Desktop is already running — e.g. created by an external codex app-server client process sharing the same CODEX_HOME — the thread is discoverable in the Desktop search (Ctrl+G) but opening it shows an endless loading view that never renders. This affects every navigation path tested: clicking the search result and navigating via the codex://threads/<id> deep link both land on the same broken view (see the deep-link test comment below). At view activation the renderer logs resumeState=null (instead of needs_resume as for threads that open fine), it never initiates a resume (no maybe_resume_started event, no thread/resume request), and the view spins forever. After the next full Desktop restart, the exact same thread is classified needs_resume and opens normally.

Observed rule, across all tested combinations:

  • Thread known to Desktop at app startup → needs_resumemaybe_resume_startedthread/resume OK (~6s) → renders. Regardless of which client created it, its CLI version, or whether threadSource was passed.
  • Thread appearing after startup (created mid-session by an external app-server process) → resumeState=null → resume never initiated → endless spinner until the app is restarted.

Desktop log evidence (first app session started 07:02, never restarted in between; second session started 09:13 after a manual restart):

Opening a pre-existing user thread works — renderer marks it needs_resume and resumes within ~6s:

2026-07-02T07:16:50.219Z info [electron-message-handler] thread_stream_view_activity_changed active=true conversationId=019f1e8b-5f34-7f31-99ce-ae46166c2ac1 ... resumeState=needs_resume streamRole=null
2026-07-02T07:17:04.444Z info [AppServerConnection] response_routed ... conversationId=019f1e8b-5f34-7f31-99ce-ae46166c2ac1 durationMs=5624 errorCode=null method=thread/resume ...

Opening the externally-created thread (created at 08:16 by the external app-server, i.e. during this Desktop session) — renderer marks it resumeState=null, two attempts:

2026-07-02T08:57:00.136Z info [electron-message-handler] thread_stream_view_activity_changed active=true conversationId=019f2209-25a6-7a93-aca6-1697a8a93eca ... resumeState=null streamRole=null
2026-07-02T09:00:47.030Z info [electron-message-handler] thread_stream_view_activity_changed active=true conversationId=019f2209-25a6-7a93-aca6-1697a8a93eca ... resumeState=null streamRole=null

Direct proof that no resume is ever initiated for these threads. The renderer logs a send-side maybe_resume_started event when it initiates a resume. Over the entire first app session, the exhaustive list of these events is:

2026-07-02T07:16:51.755Z maybe_resume_started conv=019f1e8b-5f34-7f31-99ce-ae46166c2ac1
2026-07-02T07:39:35.226Z maybe_resume_started conv=019f21c3-dd67-7aa1-94d7-75d29f36b580
2026-07-02T07:52:52.923Z maybe_resume_started conv=019f21d0-9615-7951-9f50-440472a37b62
2026-07-02T08:14:46.315Z maybe_resume_started conv=019efa6e-384d-7b52-b3eb-2df2fd3d5864
2026-07-02T08:42:33.713Z maybe_resume_started conv=019f21fe-04b9-7f00-8dc9-ed59a30ad8d3

All five are threads that open fine. None of the externally-created threads appears, despite the user opening them at 07:20, 08:24, 08:57 and 09:00. Likewise, the exhaustive list of method=thread/resume responses in that session contains exactly two entries (07:17:04 and 08:14:56), both for healthy threads.

Positive control after restarting Desktop — the same threads now follow the full healthy path (second app session, started 09:13):

2026-07-02T09:13:46.377Z ... thread_stream_view_activity_changed active=true conversationId=019f2209-25a6-7a93-aca6-1697a8a93eca ... resumeState=needs_resume ...
2026-07-02T09:13:47.510Z ... maybe_resume_started conversationId=019f2209-25a6-7a93-aca6-1697a8a93eca ... previousResumeState=needs_resume ...
2026-07-02T09:13:57.382Z ... response_routed ... conversationId=019f2209-25a6-7a93-aca6-1697a8a93eca durationMs=5590 errorCode=null method=thread/resume ...
2026-07-02T10:13:14.746Z ... maybe_resume_success assignedStreamRole=owner conversationId=019f21e6-2984-78d2-86dd-2b0b2c02e8e8 ... latestTurnStatus=completed ...
2026-07-02T10:15:44.253Z ... maybe_resume_success assignedStreamRole=owner conversationId=019f1e35-a454-7910-b1f1-8bc3c36b5d9d ... latestTurnStatus=completed ...

(The last two are the threads created by CLI 0.142.4 without thread_source — they also open normally in the post-restart session, which is what invalidated the original report's threadSource theory.)

The threads themselves are perfectly resumable at all times. From a fresh, independent codex app-server (0.142.5), while Desktop was still failing to open the thread:

thread/resume {"threadId":"019f2209-25a6-7a93-aca6-1697a8a93eca"}
→ OK after 14506ms: turns=1, threadSource=user, name=<thread name>

thread/list from that instance also returns the thread with status={"type":"notLoaded"} — indistinguishable from threads that Desktop opens fine. The rollout JSONL ends with task_complete, the thread row is present in state_5.sqlite with the correct rollout_path and name, and the name is present in session_index.jsonl at click time — so this is not an indexing or data-corruption problem.

One data point needing interpretation: a day-old external thread (019f1e35-…, whose app-server had been hard-killed before a clean shutdown) still showed resumeState=null at 07:20 in the fresh 07:02 session. Hypothesis: its state-DB row had not yet been written/reconciled at 07:02 startup (the killed app-server never persisted it), so from Desktop's perspective it too "appeared mid-session". After the 09:13 restart it opens normally.

What steps can reproduce the bug?

  1. Start Codex Desktop and leave it running.
  2. From another process, spawn codex app-server (npm @openai/codex 0.142.5) and run a complete thread over JSON-RPC on stdio:
{"id":1,"method":"initialize","params":{"clientInfo":{"name":"my_automation","title":"My Automation","version":"0.1.0"},"capabilities":{"experimentalApi":true}}}
{"method":"initialized","params":{}}
{"id":2,"method":"thread/start","params":{"cwd":"C:\\some\\workspace","threadSource":"user","approvalPolicy":"on-request","approvalsReviewer":"auto_review","sandbox":"workspace-write"}}
{"id":3,"method":"thread/name/set","params":{"threadId":"<threadId>","name":"my thread name"}}
{"id":4,"method":"turn/start","params":{"threadId":"<threadId>","input":[{"type":"text","text":"..."}]}}
  1. Wait for turn/completed, close stdin, and let the app-server exit cleanly (exit code 0).
  2. In the running Desktop instance: Ctrl+G, search the thread name → it is found. Click it.
  3. Actual: the thread view loads forever (resumeState=null, no resume initiated). Repeated attempts behave the same.
  4. Quit and relaunch Codex Desktop, open the same thread again.
  5. Actual after restart: the thread opens normally with full history.

Affected session ids on my machine, if useful:

  • 019f2209-25a6-7a93-aca6-1697a8a93eca — CLI 0.142.5, threadSource:"user": unopenable while the Desktop session it was created under was running; opens after restart.
  • 019f21e6-2984-78d2-86dd-2b0b2c02e8e8 and 019f1e35-a454-7910-b1f1-8bc3c36b5d9d — CLI 0.142.4, no thread_source: same behavior (opens in the post-restart session).
  • 019f1e8b-5f34-7f31-99ce-ae46166c2ac1 — normal user thread used as the working control case.

What is the expected behavior?

Opening a persisted, healthy thread from search should behave the same regardless of which app-server client created it and when: Desktop should classify it as resumable (needs_resume), issue thread/resume, and render the history — exactly what it already does for threads known at startup, and what codex resume --include-non-interactive <threadId> does successfully in the CLI for these same threads.

Concretely: threads appearing on disk (shared CODEX_HOME) after Desktop startup, created by another app-server process, should be picked up by whatever reconciliation feeds the UI thread catalogue, instead of landing in a state where the view waits indefinitely.

Additional information

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appapp-serverIssues involving app server protocol or interfacesbugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions