Before submitting
Area
apps/server
Steps to reproduce
- In the desktop app, start threads on worktrees and let a codex provider turn run (long tasks, several minutes in).
- While the turns are running, the desktop app process exits and a new instance starts. Any restart path hits this: manual relaunch, crash, or an updater cycling the app. No Stop is pressed at any point.
- Relaunch completes. Open the sidebar.
- Send a message to one of the affected threads.
Three concurrent codex threads hit this in one event, so it reproduces reliably when the app dies mid-turn.
Expected behavior
On startup the server reconciles session rows marked running against provider sessions that are actually alive. A thread whose provider process died with the app should surface as interrupted or failed, accept new messages, and either deliver or bounce anything queued.
Actual behavior
The threads show Working forever. The timer keeps counting (observed past 3h55m), messages sent to the thread queue and never deliver, and there is no stop or steer affordance to break out. Restarting the app again does not clear it.
The persisted state shows the mismatch. Hours after the provider sessions died, state.sqlite still reports for all three threads:
projection_thread_sessions.status = running
active_turn_id IS NOT NULL
while the codex rollout files under ~/.codex/sessions/ for those same sessions had received no writes since the moment the app restarted, and each ends mid-turn (last records are a reasoning item or tool output, no task_complete).
This looks related to #4561 but is a different path: #4561 needs an explicit Stop before quit and describes a durable stopped state that never replays. Here nothing was stopped. The turns were live when the process died, so no terminal state was persisted at all, and nothing at startup notices that the running rows point at provider sessions that no longer exist.
Impact
Major degradation or frequent failure
Version or commit
v0.0.29-nightly.20260725.899 (desktop AppImage)
Environment
Ubuntu 24.04.4, desktop AppImage, codex-cli 0.145.0 app-server provider
Logs or stack traces
# journal: new app instance starts while three codex turns are mid-flight
Jul 26 10:04:46 systemd[2035]: Started app-t3code-3992110.scope.
# rollout files for the three sessions: mtime frozen at the restart, no task_complete
# (checked ~4h later, threads still shown as Working)
-rw-rw-r-- 656155 Jul 26 10:04 rollout-2026-07-26T09-53-33-<uuid-1>.jsonl
-rw-rw-r-- 501651 Jul 26 10:04 rollout-2026-07-26T09-54-24-<uuid-2>.jsonl
-rw-rw-r-- 496560 Jul 26 10:04 rollout-2026-07-26T09-55-08-<uuid-3>.jsonl
# state.sqlite, ~4h after the restart, same three threads
sqlite> select s.status, s.provider_name, s.active_turn_id is not null
from projection_thread_sessions s join projection_threads t using(thread_id)
where t.thread_id in (...);
running|codex|1
running|codex|1
running|codex|1
Workaround
Detection: compare the rollout file mtime under ~/.codex/sessions/YYYY/MM/DD/ against the sidebar timer. Hours-stale mtime plus status = running in state.sqlite means the thread is a ghost.
Recovery: the work is salvageable outside the app. codex exec resume <session-uuid> (uuid from the rollout filename) resumes the dead session headless with full context, and the worktree still holds any uncommitted files. The ghost thread itself can only be archived. Nothing clears its Working state.
Before submitting
Area
apps/server
Steps to reproduce
Three concurrent codex threads hit this in one event, so it reproduces reliably when the app dies mid-turn.
Expected behavior
On startup the server reconciles session rows marked running against provider sessions that are actually alive. A thread whose provider process died with the app should surface as interrupted or failed, accept new messages, and either deliver or bounce anything queued.
Actual behavior
The threads show Working forever. The timer keeps counting (observed past 3h55m), messages sent to the thread queue and never deliver, and there is no stop or steer affordance to break out. Restarting the app again does not clear it.
The persisted state shows the mismatch. Hours after the provider sessions died,
state.sqlitestill reports for all three threads:while the codex rollout files under
~/.codex/sessions/for those same sessions had received no writes since the moment the app restarted, and each ends mid-turn (last records are a reasoning item or tool output, notask_complete).This looks related to #4561 but is a different path: #4561 needs an explicit Stop before quit and describes a durable stopped state that never replays. Here nothing was stopped. The turns were live when the process died, so no terminal state was persisted at all, and nothing at startup notices that the running rows point at provider sessions that no longer exist.
Impact
Major degradation or frequent failure
Version or commit
v0.0.29-nightly.20260725.899 (desktop AppImage)
Environment
Ubuntu 24.04.4, desktop AppImage, codex-cli 0.145.0 app-server provider
Logs or stack traces
Workaround
Detection: compare the rollout file mtime under
~/.codex/sessions/YYYY/MM/DD/against the sidebar timer. Hours-stale mtime plusstatus = runninginstate.sqlitemeans the thread is a ghost.Recovery: the work is salvageable outside the app.
codex exec resume <session-uuid>(uuid from the rollout filename) resumes the dead session headless with full context, and the worktree still holds any uncommitted files. The ghost thread itself can only be archived. Nothing clears its Working state.