macOS Codex app: restored thread is idle but paginated history persists final turn as inProgress

Open 💬 1 comment Opened Aug 25, 2026 by mr-ryan-james
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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

  1. A long local Desktop conversation had completed earlier tool and child-worker activity.
  2. 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
``

  1. The turn continued far enough for later reasoning/tool events and a final successful command result to be persisted.
  2. No final assistant message, task_complete, turn_aborted, or other terminal turn event was found in the persisted rollout after the last result.
  3. 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:

  1. A long-running local conversation was open in Codex Desktop on macOS, and the app's paginated-history projection was present.
  2. A turn persisted normal events and a completed tool result.
  3. The model stream had disconnected earlier in the turn, and no terminal turn event was persisted after the last result.
  4. Desktop was fully quit and reopened, then the conversation was opened again.
  5. In the observed incident, the paginated history projection retained inProgress even though thread/read reconstructed the turn as interrupted and 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_check returned ok for 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:

  1. initialize / initialized
  2. thread/unarchive
  3. thread/read with includeTurns=true
  4. thread/resume
  5. thread/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/inProgress after an interrupted turn. This report adds a persisted projection/runtime contradiction after a successful tool result and after recovery through thread/read/thread/resume.
  • #36926 demonstrates an interrupted turn remaining registered as active in app-server history, but its reproducer is a repeated turn/interrupt request and TUI freeze.
  • #26174 covers a stale rollout path after unarchive, but not a turn that remains persisted as inProgress while 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.

View original on GitHub ↗

1 Comment

github-actions[bot] contributor · 3 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #40552

Powered by Codex Action