macOS Codex app: restored thread is idle but paginated history persists final turn as inProgress
What version of the Codex App are you using (From “About Codex” dialog)?
26.818.61809 (build 7019)
What subscription do you have?
Pro
What platform is your computer?
Darwin 25.5.0 arm64 arm
What issue are you seeing?
Summary
One long-running local conversation in the macOS Codex desktop app became stuck after a sampling stream disconnected during its final follow-up turn. The durable transcript and SQLite databases passed integrity checks, but the final turn remained persisted as inProgress with no terminal metadata. The bundled app-server later reconstructed that historical turn as interrupted and restored the thread runtime as idle; the disconnection is an observed precursor, not a proven root cause.
This left three lifecycle views in disagreement:
paginated history projection: turn = inProgress
app-server reconstructed history: turn = interrupted
app-server live thread runtime: thread = idle, canAcceptDirectInput = true
The Desktop UI behaved as if the conversation were still incomplete: it loaded inconsistently and could not reliably resume the conversation through its normal UI, despite the later app-server runtime reporting an idle, input-capable thread.
Sanitized observed sequence
- A long local Desktop conversation had completed earlier tool and child-worker activity.
- During a final follow-up turn, the sampling stream disconnected. The local log recorded:
``text``
stream disconnected - retrying sampling request ... sampling_error=stream disconnected before completion: failed to send websocket request: Connection closed normally
- The turn continued far enough for later reasoning/tool events and a final successful command result to be persisted.
- No final assistant message,
task_complete,turn_aborted, or other terminal turn event was found in the persisted rollout after the last result. - After reopening the thread, Desktop did not reconcile the orphaned turn into a recoverable terminal state.
The final row in the paginated history projection remained:
status = inProgress
started_at = set
completed_at = NULL
duration_ms = NULL
final_agent_item_id = NULL
rollout_end_ordinal = NULL
rollout_end_byte_offset = NULL
error_json = NULL
After recovery through the bundled app-server, thread/read with includeTurns=true reconstructed the turn as interrupted, and thread/resume reported the thread as idle and able to accept input. However, the persisted history row still remained inProgress with the terminal fields above unset.
What steps can reproduce the bug?
This was observed in one real local thread; I do not yet have a deterministic public reproducer. The conditions observed in this incident were:
- A long-running local conversation was open in Codex Desktop on macOS, and the app's paginated-history projection was present.
- A turn persisted normal events and a completed tool result.
- The model stream had disconnected earlier in the turn, and no terminal turn event was persisted after the last result.
- Desktop was fully quit and reopened, then the conversation was opened again.
- In the observed incident, the paginated history projection retained
inProgresseven thoughthread/readreconstructed the turn asinterruptedand the resumed runtime had no active turn.
The affected conversation was also archived during an attempted recovery step; this was not the initial trigger. Using thread/unarchive restored the thread to the active list but did not reconcile the stale inProgress history row.
No session/thread identifier, prompt, project content, local path, or artifact name is included because this is a public issue.
What is the expected behavior?
When Desktop starts, reads, resumes, or unarchives a thread, an orphaned inProgress turn with no live request/stream owner should be reconciled atomically to a recoverable terminal state such as interrupted or failed.
In particular:
- reconstructed turn history, the paginated history projection, aggregate thread state, and Desktop UI should agree;
- terminal timestamps/end offsets should be finalized consistently;
- archive/unarchive and resume should not leave stale lifecycle fields in the history projection;
- the composer should become usable when the runtime is actually idle; and
- if automatic reconciliation is unsafe, Desktop should expose a clear user-visible Recover/Retry/Continue action instead of leaving the conversation stuck.
Additional information
Integrity checks
Before attempting recovery, I made a backup and performed read-only checks:
- the rollout contained thousands of valid JSONL records and was internally well-formed;
- every line parsed, the file ended with a newline, and there were no NUL bytes;
- the stored projection offset matched the exact rollout EOF;
- every persisted history item contained valid JSON;
- SQLite
PRAGMA quick_checkreturnedokfor the state, paginated history, logs, goals, memories, and queue databases; - related child threads had completed normally; and
- there was no live writer for the old thread and no relevant database-corruption or crash indication.
These checks found no evidence of malformed rollout JSONL or general SQLite corruption; the observed state is consistent with a lifecycle/projection reconciliation failure.
Manual recovery performed
I did not manually edit SQLite or delete the conversation. After backing up the affected rollout and local state files, I used the app-server bundled with the macOS app over stdio:
initialize/initializedthread/unarchivethread/readwithincludeTurns=truethread/resumethread/unsubscribe, then a clean server exit
The documented app-server protocol calls moved the rollout back to the active sessions location, cleared the archived metadata, reconstructed the last turn as interrupted, and restored an idle/resumable runtime. This made the conversation usable again. The stale history-projection row nevertheless remained inProgress, so the persisted inconsistency was not actually repaired.
The inProgress projection row was observed before the diagnostic app-server calls and remained unchanged afterward; those calls did not create the inconsistency. Affected session runtime metadata recorded 0.149.0-alpha.4.1; the bundled recovery app-server reported 0.149.0-alpha.4.3. App-server protocol reference: https://learn.chatgpt.com/docs/app-server
Related reports and distinction
- #28499 reports a macOS Desktop thread remaining active/
inProgressafter an interrupted turn. This report adds a persisted projection/runtime contradiction after a successful tool result and after recovery throughthread/read/thread/resume. - #36926 demonstrates an interrupted turn remaining registered as active in app-server history, but its reproducer is a repeated
turn/interruptrequest and TUI freeze. - #26174 covers a stale rollout path after unarchive, but not a turn that remains persisted as
inProgresswhile reconstruction and runtime are terminal/idle.
Privacy
Project subject matter, prompts, working-directory paths, filenames, artifacts, conversation identifiers, account details, and raw logs have been intentionally omitted. I can provide a narrower sanitized lifecycle trace privately if maintainers identify a secure channel and the specific fields needed.
1 Comment
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action