Repository navigation
Paginated history drops valid flattened rollout records and reuses ordinals #35746
Description
What version of Codex CLI is running?
Observed on 0.146.0-alpha.10.1. The affected source paths remain unchanged in rust-v0.146.0-alpha.14.
What platform is your computer?
Linux x86_64.
What issue are you seeing?
Paginated rollout history has inconsistent RolloutLine decoding. Some readers deserialize JSON directly into RolloutLine, while the canonical loader first parses into serde_json::Value and then calls serde_json::from_value::<RolloutLine>.
A serialized RolloutLine::EventMsg(EventMsg::TokenCount(...)) containing populated rate-limit and credit data is valid JSON and succeeds through the value-first path, but direct serde_json::from_str::<RolloutLine> / serde_json::from_slice::<RolloutLine> rejects it.
Two paginated-history readers currently use the direct path:
codex-rs/rollout/src/ordinal.rs:scan_next::<RolloutLine>()codex-rs/thread-store/src/local/thread_history_materialization.rs:serde_json::from_slice(line_bytes)
This produces two related failures:
- Resume ordinal discovery silently skips the valid final record, steps back to an earlier ordinal, and can append a new record with an already-used ordinal.
- SQLite history projection silently skips the valid record while advancing its byte offset beyond it, leaving the materialized projection behind the canonical JSONL permanently.
Once affected, catch-up can begin with one replayed boundary ordinal equal to projection_state.next_ordinal - 1; rejecting that boundary prevents the remaining valid suffix from materializing.
What steps can reproduce the bug?
Add a focused test that serializes a paginated RolloutLine containing a token-count event with rate-limit and credit fields:
let encoded = serde_json::to_string(&RolloutLine {
timestamp: "2026-07-09T00:00:05Z".to_string(),
ordinal: Some(5),
item: RolloutItem::EventMsg(EventMsg::TokenCount(TokenCountEvent {
info: Some(TokenUsageInfo::full_context_window(1_000)),
rate_limits: Some(RateLimitSnapshot {
limit_id: Some("limit-1".to_string()),
limit_name: None,
primary: Some(RateLimitWindow {
used_percent: 1.0,
window_minutes: Some(60),
resets_at: Some(1),
}),
secondary: None,
credits: Some(CreditsSnapshot {
has_credits: true,
unlimited: false,
balance: Some("1".to_string()),
}),
individual_limit: None,
spend_control_reached: None,
plan_type: None,
rate_limit_reached_type: None,
}),
})),
})?;
assert!(serde_json::from_str::<RolloutLine>(&encoded).is_err());
assert!(
serde_json::from_str(&encoded)
.and_then(serde_json::from_value::<RolloutLine>)
.is_ok()
);Then:
- Append that record as the final line of a paginated rollout and resume it. Ordinal discovery ignores ordinal
5. - Materialize the same rollout into the thread-history SQLite projection. The line is rejected, but the stored byte offset advances past it.
What is the expected behavior?
Every rollout reader should use the same value-first decoding semantics as the canonical loader. Resume should continue after the real final ordinal, and SQLite materialization should project every valid canonical record.
For projections already affected by the old decoder, narrowly accepting and skipping one replayed first boundary ordinal (next_ordinal - 1) allows the remaining suffix to catch up without rewriting canonical JSONL.
Additional information
rust-v0.146.0-alpha.14 changes nearby thread-list behavior for missing rollout paths, but ordinal.rs and thread_history_materialization.rs still use direct RolloutLine deserialization.
Activity
erichanwang commentedon Jul 28, 2026
i traced this to the two direct
RolloutLinedecode sites called out here: reverse ordinal discovery and thread-history materialization both bypass the value-first decode path used by canonical rollout loading.local tested fix: make those readers parse the physical JSON line as
serde_json::Valuefirst, then decode theRolloutLine, preserving the existing behavior of skipping malformed physical lines.validation on my branch:
just fix -p codex-rolloutjust fix -p codex-thread-storejust test -p codex-rollout(113 passed)just test -p codex-thread-store(158 passed)because
docs/contributing.mdsays external PRs are invitation-only, i did not open a PR. branch is ready on my fork if maintainers want one: https://github.com/erichanwang/codex/tree/fix/paginated-rollout-value-decodeAlarmingMelon commentedon Aug 2, 2026
Confirmed on ChatGPT/Codex for Windows.
Feedback ID: 019fb9de-3796-71e2-b216-b709023f8dbe
Codex app: 26.727.6591.0
CLI/app-server: 0.146.0-alpha.9.2
The raw rollout remains valid with 2,200+ continuous ordinals, but the UI history projection is frozen immediately before ordinal 20. Logs repeatedly report:
invalid type: map, expected f64
thread history projection ... expected ordinal 20, got 21
Impact:
Current-session diagnostics were included with the feedback submission; browser-tab logs were excluded.
AlarmingMelon commentedon Aug 2, 2026
Two additional Codex for Windows tasks now reproduce the same projection failure on the same installed build:
26.727.6591.00.146.0-alpha.9.2Additional affected task 1
019fc3d5-e9dd-7f51-be4d-8557ffb5a2bd0-154, no malformed JSON, and no gaps or duplicate ordinals.thread_history_projection_stateremained frozen at byte offset235257, next ordinal17, with only four projected items.invalid type: map, expected f64thread history projection ... expected ordinal 17, got 18Additional affected task 2
019fbf98-130c-7e62-b16e-038a76c604010-431, no malformed JSON, and no gaps or duplicate ordinals.739950, next ordinal17, again with only four projected items.expected ordinal 17, got 18.For both tasks, the app-level read-only history call also fails with:
paginated threads do not support thread/read(includeTurns=true)User-visible result: reopening either task displays only the initial user input and one assistant response despite the intact canonical rollout.
Negative control: in the same app session, the user reopened two other threads under another project and both appeared to load normally. This suggests the defect is not triggered visibly for every task, even on the same client installation.
Impact now extends beyond missing UI history: substantial weekly model usage is being consumed diagnosing the client and reconstructing work that should remain visible.
Please ensure the eventual repair handles already-stalled projections and the replayed boundary ordinal, rather than only correcting decoding for newly created projections.
AlarmingMelon commentedon Aug 3, 2026
A fourth affected task reproduces the same decoder/projection failure and adds a potentially important cross-device trigger.
019fc4f1-b75b-70c0-a8d5-c02fbc9c000326.727.6591.00.146.0-alpha.9.2Cross-device sequence
Read-only diagnostics on the Windows host
thread_history_projection_state: byte offset439027, next ordinal179,10,12, and15token_countevent whoserate_limitsvalue is the structured map already implicated in this issueinvalid type: map, expected f64thread history projection ... expected ordinal 17, got 18paginated threads do not support thread/read(includeTurns=true)The first matching projection errors were logged at approximately 8:20:16 PM ET, closely matching the remote-open event. This suggests that cross-device viewing of an active task may initiate the paginated history materialization path and expose the decoder defect, while the originating client temporarily retains a complete live/in-memory view.
The remote view did not corrupt the canonical rollout, but once the originating client navigates away, its complete in-memory representation is expected to be replaced by the same stalled projection. In practical terms, the raw JSONL being recoverable does not eliminate the user impact: reconstructing the missing turns, ordering, tool outputs, and conclusions still consumes substantial time and model usage.
cis-aconiticacid commentedon Aug 11, 2026
I can confirm this on Linux x86_64 with Codex CLI/app-server
0.147.0. It happened in one of my long paginated threads that I use through both Remote Control and CLI resume.I inspected my original rollout JSONL and found three fully preserved occurrences of the same sequence:
14368event_msg/token_countevent_msg/thread_settings_applied17761event_msg/token_countevent_msg/thread_settings_applied17809event_msg/token_countevent_msg/thread_settings_appliedIn all three cases, the
token_countrecord contains populated structuredrate_limitsandcreditsfields. The followingthread_settings_appliedrecord then reuses exactly the same ordinal.After I repaired those three duplicates and rebuilt the projection, the same problem occurred again in the same thread under
0.147.0:For this recurrence, the paginated projection stopped at:
The next physical record at that offset still had ordinal
43706, so the app-server repeatedly reported:What I observed as a user:
codex doctor --summaryreported healthy authentication, WebSocket connectivity, app-server state, and SQLite integrity.Based on my evidence, this matches the resume ordinal-discovery failure described in this issue more closely than a simple concurrent-writer explanation. A sequential resume appears sufficient: when the final structured
token_countis rejected by directRolloutLinedeserialization, ordinal discovery steps back and the resumed writer reuses the previous ordinal.The current
mainimplementation also still appears to use:while
ReverseJsonlScanner::finish_record()directly calls:The projection reader received value-first and malformed-rollout resilience changes through #36188, but resume ordinal discovery still appears vulnerable. Once a duplicate has been written, the projector also still rejects it instead of recovering the remaining valid suffix.
I also encountered the related Remote/writer-handoff behavior discussed in #37403, #37450, and #37552. Those issues seem relevant to how the problem is triggered and presented through Remote Control, but the persisted duplicate ordinals in my rollout match #35746 specifically.
I have a sanitized audit, Doctor summary, timestamps, hashes, and a read-only reproduction script available. I am not attaching my complete rollout publicly because it contains private conversation and tool history.
nos1609 commentedon Aug 13, 2026
Independent current reproduction on Linux x86_64 / VS Code Web with Codex CLI/app-server
0.147.0.Thread/Feedback ID for internal trace lookup:
019fe127-1fbf-7423-92d4-98d1f9405449.The canonical paginated rollout is valid JSONL, but it contains two preserved resume-boundary ordinal reuse sequences:
and later:
The first duplicate permanently stalled the SQLite history projection at:
Every later live projection attempt reports:
This repeated under two successive app-server processes, so restarting the affected task/app-server did not repair it.
At inspection time, the raw rollout still contained 41,517 records (about 307 MB) and continued through ordinal 41,514. Thus more than 11,000 later physical records remained preserved in canonical storage but were absent from the paginated projection and effectively disappeared from the reopened UI history.
This confirms that stable
0.147.0remains affected in both parts described here:token_countordinal.next_ordinal - 1.The user-visible missing-history symptom and side/fork failures are the same failure class as #38248. Fixing only new decoding is insufficient; existing affected projections also need a supported rebuild or the narrow replay-boundary recovery described in this issue.
No project name, repository data, prompts, local paths, host/user identifiers, process identifiers, or message contents are included.
nos1609 commentedon Aug 13, 2026
Recovery outcome from the same sanitized reproduction:
169588767/ next ordinal30092, and exposed 59 turns.306891319/ next ordinal41514.thread/turns/listnow returns all 79 turns in one page with no projection error.quick_checkisok, and the repaired ordinal stream has no gaps or non-increasing values.This recovered 20 previously invisible turns without reconstructing user or assistant messages. It confirms the missing history was a stale derived projection, not loss of the underlying conversation.
The manual repair required a full backup and unsupported local storage surgery. The product still needs a built-in rebuild/recovery path for already affected projections.
No paths, project or repository names, prompts, message contents, host/user identifiers, or process identifiers are included.
AaronZLT commentedon Aug 14, 2026
need fix
nos1609 commentedon Aug 15, 2026
Third observed incident in the same sanitized Linux x86_64 / VS Code Web thread with CLI/app-server
0.147.0.Thread/Feedback ID:
019fe127-1fbf-7423-92d4-98d1f9405449.The failure returned after each successful manual recovery:
30091and41264. Recovery advanced the projection to EOF306891319and next ordinal41514.47173and49862. The projection stopped at byte380474094and next ordinal47174, while canonical storage grew to419985119.event_msg/token_count(51563)event_msg/thread_settings_applied(51563)event_msg/task_started(51564)The projection remained at the previous repaired EOF
419985119and next ordinal51564, while canonical storage grew to457057512. The app-server repeatedly logged:expected ordinal 51564, got 51563For the third recovery, removing only the duplicate metadata record and running the normal projection catch-up restored:
457057512545800-54579thread/turns/list: 93 turns, no next cursor, no projection errorquick_check:okNo user or assistant message required reconstruction. A successful repair is therefore not durable: a later sequential resume in the same thread can create another duplicate and freeze the projection again.
The product needs both:
token_countnext_ordinal - 1replay boundaryThis report contains no private paths, project or repository names, prompts, message contents, host or user identifiers, or process identifiers.
liambern commentedon Aug 15, 2026
Independent confirmation on stable Codex CLI/app-server
0.147.0, Linux x86_64, from two long paginated threads.Both canonical rollouts contained the resume-boundary ordinal reuse described in this issue:
The duplicated ordinals were
1771in one rollout and6329in the other. Both duplicate boundaries occurred immediately after a structuredtoken_countrecord.User-visible behavior:
These two incidents also contained NUL-only physical JSONL records at abrupt host-restart boundaries: two in the first rollout and one in the second. The SQLite database itself passed its integrity check, but its projection cursor no longer described a usable continuation of the rollout. This produced a second failure shape beyond the simple
next_ordinal - 1replay: the saved byte offset landed inside a later physical record, so one rejected fragment was followed by an ordinal gap that the projector treated as fatal.Recovery result after backing up both stores:
65,679,285, next ordinal11,354, exposing 96 turns;86,424,395, next ordinal10,152, exposing 130 turns;ok;This confirms that stable
0.147.0can preserve the recent conversation in canonical storage while resume/fork becomes permanently stuck on the derived projection. Fixing the two inconsistent decode sites prevents new ordinal reuse, but already affected projections also need an automatic rebuild/recovery path when the saved byte boundary or ordinal invariant no longer matches the rollout.No rollout files, prompts, message contents, private paths, project names, host/user identifiers, or thread IDs are included.
Loong0x00 commentedon Aug 15, 2026
Independent current reproduction on Linux x86_64 with a paginated thread created by stable Codex CLI/app-server
0.147.0(not migrated from 0.146.x).The stale resume was visible in both Codex CLI and the ChatGPT desktop Codex surface using the shared local history state. Environment details:
0.147.026.803.81509-10.147.0-alpha.6.6Sanitized measurements from the affected thread:
thread_settings_applied(7327), followingtoken_count(7327)token_count(9090)followed bythread_settings_applied(9090)The app-server repeatedly logged:
This is not only unrepaired state originating in 0.146.x: the session metadata records
cli_version: 0.147.0at creation, and the duplicate boundaries were appended during later resumes under 0.147.0. This is also a concrete counterexample to the "pure 0.147.0 threads were clean" observation in #38792.Recovery exposed an additional self-healing requirement. After aligning the first replayed boundary, projection advanced to the second duplicate but rolled the transaction back. A one-boundary tolerance is therefore insufficient when one unprojected suffix contains multiple replayed boundary ordinals. I recovered it by committing projection up to the second boundary, aligning that boundary, then projecting the remainder.
Post-recovery verification:
PRAGMA quick_check:okfor both state and thread-history databasesPlease treat the writer bug and recovery path as shared app-server/history-store behavior rather than CLI-UI-only. Automatic repair needs to handle multiple replayed boundary ordinals in a single suffix.
Loong0x00 commentedon Aug 15, 2026
Second independent recurrence on exact
codex-cli 0.147.0, after repairing the projection to an exact JSONL EOF:6,937,927, next ordinal12302, withPRAGMA quick_check = ok.12408: populatedtoken_count12408:thread_settings_applied12409:task_started7,289,327with next expected ordinal12409, because the next physical record was the duplicate12408.12,125,753bytes, so4,836,426bytes (including 19 assistant messages) were present on disk but invisible after reopening.I also reproduced the writer failure directly from the
rust-v0.147.0tag with a focused test whose final durable line is a populated flattenedtoken_countat ordinal 5:[0, 5, 1]scan_next::<serde_json::Value>()followed byserde_json::from_value::<RolloutLine>():[0, 5, 6]codex-rolloutcrate: 115/115 tests passed; scoped Clippy passedThis confirms that a clean projection repair is immediately undone by the first affected resume on 0.147.0, and that the value-first ordinal decode fixes the reproducer. Existing affected projections still need the narrow boundary catch-up described in the issue.
17 remaining items
nos1609 commentedon Aug 28, 2026
New 0.150.1 recurrence:
task_starteditself reuses the terminaltoken_countordinalThis is another recurrence in a previously recovered long-running paginated task. The current duplicate was created after the earlier recovery. No rollout, SQLite state, or process was modified during this evidence capture.
Environment
26.825.4187.00.150.0-alpha.12.20.150.10.147.0paginatedExact stalled boundary
The canonical rollout currently contains
707,757,918bytes and79,078physical lines. The projection state is frozen at:The exact valid JSON records at the boundary are:
This differs from the previously common signature:
Here the new
task_startedrecord directly reuses the priortoken_countordinal. The token-count record was written at2026-08-26T23:25:12.228Z; the duplicated task start was written at2026-08-27T07:09:57.713Z.A complete read-only ordinal scan found exactly one duplicate and no gaps among parseable records. Six older malformed/non-JSON physical lines exist earlier in the file, but the projector had already advanced past all of them. They do not explain this later stop.
Preserved but invisible answer
The rollout continued normally through ordinal
79070. The newest persisted turn contains:phase=final_answer;event_msg/task_completeat2026-08-28T11:05:06.141Z.The thread reports
idle, so that turn is terminal. However, Desktop and the native task-read projection still expose only older turns. The unprojected suffix is88,325,231bytes, approximately84.2 MiB, covering about8,684ordinals.The live writer logs the same failure for every later persist and final flush:
Therefore, the missing answer is not model failure or transcript deletion. It is present in the durable rollout and hidden by the stalled derived projection.
Impact
Fix direction
token_count.task_startedmust never reuse an existing ordinal, even if resume-time metadata is not emitted.token_count(N)followed directly by a latertask_started; assert that the latter isN+1.This extends the current-main reverse-reader evidence already documented above: the ordinal-reuse bug is still reproducible on stable
0.150.1, andthread_settings_appliedis not required for the bad boundary.JavieHan commentedon Aug 28, 2026
Independent macOS Desktop recurrence in two threads at the same recovery boundary
Adding a macOS confirmation from an unmodified Codex Desktop installation. No rollout or SQLite file was modified before evidence capture.
Environment
arm64)26.825.32147(build7303)codex-cli 0.150.0-alpha.12.2Two independent affected threads
Both rollouts were valid JSONL and continued receiving newer records, but their SQLite history projections stopped at the duplicate boundary.
Thread A:
Thread B:
The two reused ordinals were written only 216 ms apart, which is consistent with a shared Desktop/app-server recovery or reload boundary rather than unrelated content failures.
A read-only scan of 616 local rollout JSONL files (about 36.9 GB total) found:
Projection impact and bounded recovery
After taking an online SQLite backup, I advanced only each affected projection byte cursor past the duplicated no-body
thread_settings_appliedrecord. I did not edit either canonical JSONL.Both projections then caught up exactly to JSONL EOF,
PRAGMA quick_checkreturnedok, and the newer turns became readable again.This independently matches the reverse-reader diagnosis already documented here: a terminal structured
token_countwhoseused_percentis serialized lexically as3.0is skipped during resume ordinal discovery, so the resume event reuses the previous ordinal. The local evidence confirms the failure still affects the current macOS Desktop runtime generation and can hit multiple threads at the same recovery boundary.mulfyx commentedon Aug 29, 2026
I ran into this exact failure on Linux: the rollout JSONL was intact, but
codex resumekept loading an old projection after an ordinal was reused at a resume boundary.I packaged the recovery procedure I used into a small MIT-licensed tool:
https://github.com/mulfyx/codex-session-repair
It starts with read-only diagnosis and only offers a repair for three narrow, tested boundary shapes involving a duplicated
thread_settings_appliedortask_startedaftertoken_count. Before changing anything, it requires the target session to be closed, creates a private backup, verifies SQLite integrity, and checks that the rollout is stable. The rollout itself is never edited; the repair is limited to one guarded projection-cursor update.I tested a real recovery with
codex-cli 0.151.0on Linux. The tool deliberately refuses malformed rollouts, unknown duplicate types, schema drift, active target sessions, and ambiguous cases.This is an unofficial workaround for already-affected local sessions, not a fix for the upstream decoder or writer bug.
Preserve resume ordinals for float-bearing rollout records
Recover history projection past duplicate no-op ordinals
Preserve resume ordinals for float-bearing rollout records
Recover history projection past duplicate no-op ordinals
astral303 commentedon Sep 3, 2026
This is partially fixed in the upcoming 0.154.0 release (and the current alpha) by PR #42378. It forces decoding to go through canonical decoders, so it doesn't fail on the float. That was my failure mode that required database repair.
However, it doesn't fix the "reuses ordinals" half of the issue:
RolloutItem (
codex-rs/history/src/lib.rs:102) has no catch-all variant, so an unrecognized type — a rollout written by a newer Codex, resumed by an older one — still makes decode_rollout_line fail. Upstream then walks past that record and the recorder appends at an ordinal already on disk.Alcray commentedon Sep 5, 2026
Adding an independently tested, model-free reproduction and current stable-release results (September 5, 2026).
I see that #42378 has merged into
main. I have not tested an alpha/build containing that change, so this is confirmation of stable-release impact and a reusable regression probe—not a claim that #42378 is ineffective.Tested results
used_percent: 1.0rate_limits: nulltask_completecodex-cli 0.153.1codex-cli 0.153.3codex-cli 0.153.4, installed only in a temporary test directoryThe four-record fixture ends at ordinal
3. Afterthread/resumeandthread/settings/update, the runtime appendsthread_settings_appliedwith ordinal3again; the projector reports:Expected: append ordinal
4, then an EOF projection cursor expecting5.Additional macOS 0.153.1 minimization: preserving the rate-limit/credits objects but setting only
primary: nullalso passes. Changingprimaryto a scalar is not a workaround; that variant still fails numbering.This also occurred in real local and SSH-hosted histories: later completed turns remained in the canonical JSONL but disappeared from the reloaded UI because projection stopped at the duplicate. Backed-up, narrowly scoped cursor recovery restored the later turns without editing the transcripts, but that is recovery rather than prevention.
Standalone reproduction
The Python 3.10+ script below creates fresh temporary fixture directories and gives each child app-server its own
CODEX_HOME. It copies no real histories, configuration, plugins, or credentials. It sends noturn/startand makes no model request. Synthetic fixture metadata usescli_version: 0.153.1; the executable being tested is selected explicitly.Run with an absolute path to the executable:
On the tested affected builds, the nested-primary case fails while both controls pass, and
--checkexits 1. Test artifacts are retained under the printed temporary directory. The script does not modify the production installation.Complete tested reproduction script
Would a stable backport of #42378 be possible? It would also help to document whether users with already-written duplicate ordinals need a separate supported projection-recovery step after upgrading.
silas-yangxu commentedon Sep 5, 2026
Independent macOS arm64 confirmation on 0.153.4, with a seven-record numeric-encoding A/B reproduction
Confirmed using desktop-bundled
codex-cli 0.153.4on macOS arm64 (desktop version26.901.41600). I am adding the current stable-version result and a small synthetic regression probe to this existing issue.I see that #42378 has merged into
main. I have not tested a build containing that change; the results below describe the installed 0.153.4 binary and do not claim the merged fix is ineffective.The script below starts from a fresh isolated
CODEX_HOME, initializes the schema with the native app-server, creates seven synthetic rollout records, and runs nativemigrate-rollouts --applyto generate the paginated history and index. The last record istoken_countwithinfo: nulland a nested primary rate-limit window. It then starts a fresh app-server, resumes the synthetic thread, changes effort twice, and archives/unarchives to flush the writer. It submits no prompt and starts no model turn.The integer control changes only the final
used_percentJSON encoding from1.0to1and adjusts the projection's EOF byte offset. This edit is made after migration, because the native migration serializes the number back to1.0.used_percentencoding1.01The failing run logs:
In both controls, resume leaves the original file bytes unchanged and later writes preserve the original prefix. Neither run emits a
turn/startednotification. Thus this reproduction does not require an old user database, old-version session metadata, concurrent writers, or a real model response.Expected: both equivalent numeric encodings must continue after the actual final persisted ordinal. Native-emitted telemetry must not cause subsequent history to become inaccessible. The result is consistent with the reverse-decoder problem described here; it is not evidence that the underlying persisted field should be an int32.
A related local investigation began because completed conversation turns disappeared after reopening despite intact canonical JSONL. No original transcripts, task identifiers, account details, or credentials are included below; the UUIDs, messages, and usage value are synthetic.
Standalone Python reproduction (float and integer controls)
Save as
minimal_repro.pyin an empty directory, then run:Use
--codex /path/to/codexif the binary is elsewhere. The script creates separate disposable directories beside itself and refuses to overwrite existing runs. It does not patch the installed executable or use the normal Codex database directory.Please consider including this numeric-encoding A/B probe in stable-release/backport validation, and documenting a supported recovery path for histories that already contain duplicated ordinals.
astral303 commentedon Sep 5, 2026
@silas-yangxu @Alcray I have a small fork of Codex at v0.153.4 stable (changes vs release v0.153.4) which includes:
If you'd like only the recovery and fixes, you can cherry-pick both of these two commits from my Codex fork on top of
rust-v0.153.4tag and build locally. I confirmed they cherry-pick cleanly.IMO chances of backport are extremely small, and no automatic recovery has been implemented as far as I can tell. So if you got bit by this, you can use my patches, or you have to ask Codex to fix your SQLite database. Then hope to not encounter this bug again until 0.154.0 is released.
yotch0928-collab commentedon Sep 6, 2026
Independent Windows confirmation on 0.153.4, with a three-record, model-free reproduction
Adding a Windows confirmation and a small runtime probe to this existing issue. I have checked that #42378 is merged into
main; I have not tested a build containing that change, so this reports the installedcodex-cli 0.153.4behavior, not a failure of the merged fix.Controlled runtime results
Tested on Windows using the installed native
codex-cli 0.153.4. The public script below was run again on September 6, 2026 with the same results:token_count,used_percent: 26[0,1,2,3][0,1,2,3]used_percent: 26.0[0,1,2,3][0,1,2,2]expected ordinal 3, got 2used_percent: 41.0[0,1,2,3][0,1,2,2]expected ordinal 3, got 226.0token event followed by an ordinaryagent_message[0,1,2,3,4][0,1,2,3,4]All initialize, resume, and settings-update calls succeeded. The appended record is
event_msg/thread_settings_applied. The float cases also log the mismatch during shutdown.The first three fixtures contain only
session_meta(0), a syntheticagent_message(1), andtoken_count(2). Each case uses a fresh isolatedCODEX_HOMEand process. The only RPCs areinitialize,thread/resume, andthread/settings/update; closing stdin flushes the recorder. Noturn/start, model generation, forced crash, original conversation, or copied credentials are needed. This isolates terminal numeric encoding as a sufficient trigger; a crash is not necessary to reproduce it.Observed user impact and recovery
In the affected desktop conversation, the canonical JSONL remained readable but had two resume-boundary ordinal duplicates, each immediately after a decimal-valued
token_count. The history projection was stuck at the first duplicate and the UI showed only an old prefix. Restarting before repair did not recover it. This matches the desktop symptom described in #41566.After backing up the data and performing a narrowly scoped local repair of those two internal records and the projection checkpoint, indexing resumed. All 94 conversation-message records present at the repair snapshot remained byte-for-byte identical. A subsequent app restart restored the visible conversation, confirmed by the user. This is recovery evidence, not a general-purpose repair recipe or a claim that the runtime bug is fixed.
Source correspondence
The 0.153.4 release source at commit
3d2ee51ca2d5db578f328aa75e20aa22c0197c9astill callsscanner.scan_next::<RolloutLine>()and skips rejected records. The existing canonical decoder documents the decimal/flattening issue. This agrees with the diagnosis already in this issue and the change in #42378.Runnable reproduction
Save as
codex-ordinal-repro.pyand run with Python 3, passing the absolute path to the Codex executable to test:It creates synthetic cases in a new temporary directory and leaves their results there. The fixture's metadata/plan fields are synthetic constants, not account information. The script is a reproduction probe: its reported boolean checks describe the affected behavior and are not pass criteria for a fixed build.
Python script (standard library only; tested on Windows)
Please include this Windows three-record probe when validating the release/backport of the canonical-decoder fix, and provide a supported recovery path for already affected paginated histories. No private conversation logs, database files, account identifiers, or personal filesystem paths are attached.
xi-zhao commentedon Sep 9, 2026
macOS 0.153.4: two/three-record offline probes reproduce one- and two-ordinal regressions, including
0.0Adding results from the installed official desktop-bundled binary, tested on September 9, 2026:
ChatGPT.app/com.openai.codex, version 26.901.51231, build 8109.codex-cli/ app-server: 0.153.4.I checked that #42378 is merged into
main. These tests exercise the installed 0.153.4 binary; I have not tested a build containing that fix. This is not a report that the merged fix fails.Controlled results
Each case uses a fresh isolated Codex data directory, a newly generated synthetic thread ID, and a local loopback provider configuration. The initial file contains only
session_metafollowed by one or twotoken_countrecords. Theirinfois null and the primary rate-limit window containsused_percent,window_minutes, andresets_at.The RPC sequence is
initialize→initialized→thread/resume→thread/settings/update→thread/unsubscribe, followed by closing stdin and waiting for the process. Noturn/start, inference, forced crash, copied credentials, or real conversation is needed.used_percentJSON encoding0[0,1,2,3]0.0[0,1,1]0.0[0,1,2,1]The appended record is
event_msg/thread_settings_appliedin all three cases. Thus a nonzero percentage is not required, and the regression depth follows the number of trailing decimal-bearing records.User-visible impact and separately validated recovery
Two local conversations exhibited the corresponding one-record and two-record regressions. In the latest case, the history API returned only five older turns even though later user messages and completed replies remained in the durable file. The stalled boundary was
token_count(522),token_count(523), thenthread_settings_applied(522), with the projector reportingexpected ordinal 524, got 522. The file contained valid JSON throughout.A backed-up copy with only top-level ordinals corrected was first verified in an isolated app-server, then applied after releasing the original writer. Native history projection recovered 13 turns and 618 items, and the history API returned the previously hidden final reply. All non-ordinal record content was compared for equality. No live SQLite rows were manually edited. This was a guarded, case-specific recovery, not a general recommendation to renumber arbitrary rollouts.
Please clarify the first desktop app build/bundled runtime that includes #42378 and the supported recovery behavior for already-affected histories. A one-time transcript recovery does not remove the initiating bug from the installed runtime. Related maintainer acknowledgment: #43142.
Self-contained Python 3 reproduction using the installed macOS bundled binary
Save this script in a new empty directory and run
python3 reproduce_tail_ordinal.py. It uses only the Python standard library. It creates three private test directories beside the script and retains their results. Rerun from another empty directory; existing test directories are intentionally not overwritten. If testing another app installation, adjust onlyBINARY.No original chat text, screenshots, real thread IDs, personal filesystem paths, credentials, databases, or raw production logs are attached.
astral303 commentedon Sep 9, 2026
The fix has been implemented since 0.154-alpha.1 (inclusive). Ask Codex to find an alpha build for the desktop app.
astral303 commentedon Sep 10, 2026
Fully fixed in 0.154.0 with recovery on resume.