Codex paginated rollout can emit duplicate ordinal after unfinished turn, permanently freezing thread history projection
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)?
The latest version as of 29th August 2026
What subscription do you have?
ChatGPT Plus
What platform is your computer?
Windows 10 latest 64 bit enrolled in extended security updates running intel lga1150 platform
What issue are you seeing?
A long-running Codex thread became permanently stuck displaying an old version of its conversation history even though its canonical paginated rollout JSONL continued to receive hundreds of MB of later events.
Forensic inspection shows that the canonical rollout itself contains a duplicate top-level ordinal at the exact byte where thread_history_1.sqlite stops materializing history.
At the first failure boundary:
Physical line 9814
ordinal: 9813
type: token_count
byte end: 143293415
Physical line 9815
ordinal: 9813 <-- duplicate
type: thread_settings_applied
Physical line 9816
ordinal: 9814
type: task_started
The thread-history projection cursor stops exactly after physical line 9814:
next_rollout_ordinal = 9814
rollout byte offset = 143293415
The materializer subsequently reports:
expected ordinal 9814, got 9813
This causes the derived thread history to remain permanently frozen even though the canonical rollout continues.
A second duplicate-ordinal discontinuity was found later in the same rollout at physical line 12831. Both discontinuities have the same observed pattern: an unfinished turn without task_complete, followed by token_count, then a later thread_settings_applied that repeats the preceding ordinal.
Restoring the original thread_history_1.sqlite restores the UI exactly to the stale cutoff. Removing the projection database results in an empty history database; normal startup does not replay the existing paginated rollout into it.
The supported migration classifier was also tested against an isolated copy:
{
"thread_id": "01a03a72-2f06-70b0-a3b8-a309fe1ba833",
"status": "already_paginated",
"bytes_processed": 0,
"message": null
}
Therefore legacy rollout migration does not repair the affected already-paginated thread.
What steps can reproduce the bug?
I do not yet have a safe deterministic minimal reproduction, but both observed failures share this sequence:
Have a paginated Codex thread with an active root turn.
The turn ends abnormally or otherwise remains unfinished without a task_complete event.
The final event associated with the turn includes a token_count.
Later begin another task in the same thread.
In the affected cases, the emitted thread_settings_applied record repeats the preceding record's top-level ordinal instead of incrementing it.
task_started then uses the ordinal that thread_settings_applied should have used.
The history materializer reaches the duplicate and rejects it with expected ordinal N+1, got N.
The canonical rollout continues growing, but the rendered/history projection remains permanently frozen at the last valid record.
I found this pattern twice in the same affected rollout. Normal completed turns immediately before the first failure instead follow:
token_count
→ task_complete
→ thread_settings_applied (next ordinal)
→ task_started (next ordinal)
I have deliberately not attempted to manufacture an unfinished turn by killing Codex because I cannot establish that forced termination is equivalent to the original failure mode.
What is the expected behavior?
Every top-level record in a paginated rollout should receive a unique monotonically increasing ordinal.
Starting a new task after an interrupted or unfinished previous turn should not cause thread_settings_applied to reuse the preceding event's ordinal.
If an incomplete turn is encountered, Codex should either recover/close that turn or otherwise continue writing a valid event stream that the thread-history materializer can consume.
A malformed event should also ideally not make all subsequent valid conversation history permanently inaccessible in the UI.
Additional information
Feedback was also submitted through the Codex app.
Feedback ID: 01a04dc0-1ee7-7b52-9fd9-7a49c827d2f8
Additional forensic observations:
Affected rollout is already history_mode = paginated.
Canonical rollout remained intact and continued well beyond the frozen projection.
First duplicate occurs immediately after the projection's final accepted byte.
A second duplicate ordinal occurs later in the same rollout with the same unfinished-turn transition pattern.
Three normal completed turns immediately preceding the first failure contain task_complete and have consecutive ordinals.
thread_history_1.sqlite accurately represents history only through the last valid ordinal before the discontinuity.
Removing the history projection DB causes Codex to create an empty replacement rather than replaying the existing paginated rollout.
codex migrate-rollouts --json --thread on an isolated copy returns already_paginated with bytes_processed: 0.
I have preserved the original affected rollout and projection databases and can provide additional sanitized logs, exact event windows, byte offsets, or database state if useful.
No canonical rollout data was modified during diagnosis.
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 preserved canonical paginated rollout JSONL around physical lines 9814–9816 and 12831, then compare those records with thread_history_1.sqlite. Check the codex migrate-rollouts result and the history materializer’s handling of duplicate ordinals. Done means interrupted turns no longer produce repeated ordinals and subsequent valid history remains available to the projection.
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
- 42/100