Side chat fork fails after interrupted turn: duplicate rollout ordinal breaks thread history projection (expected ordinal N+1, got N)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
What version of the Codex App are you using (From “About Codex” dialog)?
26.825.6671.0 (bundled codex-cli 0.151.0-alpha.7.2)
What subscription do you have?
ChatGPT Plus
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64
What issue are you seeing?
Symptom
Opening a side chat on an affected thread fails, and the same thread silently loses several rounds of conversation history in the UI after restarting the app.
Full error:
failed to prepare paginated fork
thread-store internal error
thread history projection ... expected ordinal 3802, got 3801
Environment
- Codex Desktop 26.825.6671.0 on Windows (bundled codex-cli 0.151.0-alpha.7.2)
- CODEX_HOME on a secondary drive (F:\CodexData)
Root cause (verified on disk)
The rollout writer re-emits an already-persisted ordinal after an interrupted-turn restart. In two affected threads the rollout .jsonl contains two consecutive records with the same ordinal:
- thread A: two consecutive records stamped 'ordinal: 2650' (a token_count event and, 33 seconds later, a thread_settings_applied event)
- thread B (a fork branch): two consecutive records stamped 'ordinal: 3801'
thread_history_1.sqlite -> thread_history_projection_state then holds next_rollout_ordinal one ahead of the record actually at next_rollout_byte_offset, so the projector fails on resume. Consequences: thread/fork fails for the thread, and all rollout records after the duplicated ordinal never reach the history projection (turns exist in the file but the UI never shows them).
What steps can reproduce the bug?
- Start a long-running agent turn (workspace-write sandbox).
- Force-kill the app while the turn is in progress.
- Relaunch and continue the same session.
- Open a side chat (thread/fork) on that thread.
Affected session ids: 01a055b3-ff22-7f32-8fc9-eb2113949e10, 01a05b7d-a68c-7613-9e47-d87c5723df87
Evidence: a full ordinal scan of the affected rollout files shows exactly one discontinuity each - two consecutive records sharing one ordinal (2650 in one file, 3801 in the other); all other records are strictly sequential.
What is the expected behavior?
- The rollout writer must never re-emit an ordinal that was already persisted.
- The projector should tolerate a repeated ordinal (re-anchor to the actual file sequence) instead of hard-failing, and ideally self-heal the projection.
Additional information
- Happened twice on this machine, both times after force-killing the app during long turns. A graceful UI stop did not trigger it.
- Verified workaround (no data loss): rewind the affected thread projection pointer by one ordinal in thread_history_1.sqlite (table thread_history_projection_state, column next_rollout_ordinal), then restart the app; the projector re-consumes the duplicated record and re-ingests all missing rounds, restoring the lost history and unblocking fork.
- CODEX_HOME is on a secondary drive (F:\CodexData). A doctor run otherwise reports all databases healthy.
- The duplicated ordinal appeared ~30s after the original record, right at a turn restart boundary (the writer re-used it for a thread_settings_applied event).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the rollout writer at the interrupted-turn restart boundary and the history projector consuming the affected rollout JSONL and thread_history_1.sqlite. Inspect thread_history_projection_state alongside the repeated ordinals, then verify that restart and fork processing preserve sequential history, tolerate a repeated ordinal, and restore records after the duplicate.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, sqlite
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100