codex.exe exits with code 0 mid-session; thread-store then fails "expected ordinal N, got N-1"
What version of the Codex App are you using (From “About Codex” dialog)?
OpenAI.Codex 26.818.8289.0; codex-core 0.149.0-alpha.4.3
What subscription do you have?
ChatGPT Pro ($200/month)
What platform is your computer?
Microsoft Windows NT 10.0.19045.0 x64 (Windows 10 build 19045)
What issue are you seeing?
Environment: OpenAI Codex desktop app (Microsoft Store), package OpenAI.Codex 26.818.8289.0, engine codex-core 0.149.0-alpha.4.3, Windows 10 build 19045.
During an active session, the codex.exe engine process periodically terminates with exit code 0x00000000. This is a clean exit: there is no Application Error or Windows Error Reporting event and Windows does not create a crash dump. The exit code was confirmed by attaching ProcDump to codex.exe.
After each exit, the thread whose rollout was being written becomes permanently broken. Every subsequent durable write fails:
codex_thread_store::local::live_writer ... failed to project durable rollout for <tid>:
thread-store internal error: thread history projection for <tid> expected ordinal N, got N-1
Subsequent turns then fail with:
Custom tool call output is missing for call id ...
The affected thread remains unusable. Archiving only stops additional errors because the archived thread is no longer written.
Observed scope from a read-only snapshot of logs_2.sqlite taken at 2026-08-25 15:50:27 UTC:
- 6 distinct affected threads
- 2,713 rows containing
expected ordinal - First event: 2026-08-23 21:13:23 UTC
- Most recent event: 2026-08-25 15:49:56 UTC
One exact logged failure:
handle_tool_call_with_source:dispatch_tool_call_with_code_mode_result{otel.name=apply_patch tool_name=apply_patch call_id="exec-f4c539e3-e06f-44a6-9ce4-b24617e7e213" aborted=false}:dispatch_tool_call_with_terminal_outcome:persist_rollout_items{item_count=1}:append_items{item_count=1}:append_items{item_count=1}: failed to project durable rollout for 01a02da6-8925-7412-a4b6-3bf9c0af6a98: thread-store internal error: thread history projection for 01a02da6-8925-7412-a4b6-3bf9c0af6a98 expected ordinal 6732, got 6731
This does not appear to be a network or authentication issue: backend requests return 200 OK and authentication remains valid. It also does not appear to be an OS-level crash.
A full user-mode dump captured at the exact exit moment is available on request. It may contain session data and is therefore not attached publicly.
This may be related to #40231, but the termination signature is different: that report shows 0xC000013A / STATUS_CONTROL_C_EXIT, while this report was externally observed as 0x00000000.
What steps can reproduce the bug?
- Use Codex Desktop normally in an active session.
- Continue a turn that performs local work while the rollout is being written.
- When the periodic codex.exe exit occurs mid-write, Codex Desktop restarts the engine.
- Resume or continue the affected thread.
- Durable writes repeatedly fail with the thread-history ordinal mismatch, followed by missing custom tool-call output errors.
The exit is intermittent, but every observed thread active at the exit becomes unusable afterward.
What is the expected behavior?
codex.exe should remain running during an active turn. If the process exits or is restarted, the durable thread history should recover atomically without an ordinal mismatch or a missing tool-call output, and the thread should remain usable.
Additional information
The SQLite log database was inspected through a read-only URI with query-only mode enabled. No Codex database was modified.
No public memory dump is attached. A full user-mode dump captured at the exit moment can be provided through a secure channel on request.
5 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Root-caused the second half of this (the thread-store failure after the restart). It's a resume bug, independent of why
codex.exeexited, and it's the same defect behind #40537 on macOS, where no crash is involved at all.Symptom → mechanism
expected ordinal N, got N-1means SQLite's projection checkpoint has already consumed ordinalN-1, but the next durable JSONL line carriesN-1again — the recorder re-issued an ordinal. That can only happen if the recorder, on resume, believed the final record in the rollout wasN-2.There are two different decoders reading the same JSONL:
thread-store/src/local/thread_history_materialization.rs) parses each line asserde_json::Valueand goes throughcodex_rollout::decode_rollout_line.rollout/src/ordinal.rs::ordinal_state_for_rollout) reverse-scans the tail withscanner.scan_next::<RolloutLine>()— a typed parse of a struct with#[serde(flatten)] item.decode_rollout_lineexists precisely because those disagree. Its own doc comment: "Withserde_json/arbitrary_precision, Serde's generic buffer cannot replay floating-point values nested inside flattened or internally tagged fields." That feature is enabled workspace-wide through unification —codex-protocol → codex-execpolicy → starlark → serde_json/arbitrary_precision(and again viaexec-server-protocol), so it's on incodex.exe, the CLI, and evencargo test -p codex-rollout.So when the last complete line in the rollout contains a float, the typed scan
Rejecteds it and steps back one record; the projection had accepted it. The recorder resumes atN-1, writes a valid duplicate-ordinal line, andapply_projectionrejects it (ordinal != next_ordinal). Because the recorder keeps advancing from the wrong base, every later write also fails — hence 2,713 rows and "the thread is permanently broken."The float that does it is
token_count→rate_limits.primary.used_percent(anf64, e.g.12.5;0.0also breaks — any decimal). Atoken_countevent is emitted after every model response, so it is very commonly the final record whenever the process dies between or during turns, and it's the last record when a fork is prepared from an idle thread (#40537).Reproduction
Added a unit test in
codex-rs/rollout/src/recorder_tests.rs: write a paginated rollout[meta@0, agent_message@1, token_count{used_percent: 12.5}@2], thenappend_rollout_item_to_path. Expected ordinals[0,1,2,3].Plain
cargo test -p codex-rollouton today'smain(304c8de) — no special flags needed, sincearbitrary_precisionis already unified into the crate's test build.Fix that makes the test pass
Use the same decoder on the write side that the projection uses on the read side:
rollout/src/ordinal.rs:scan_next::<serde_json::Value>()+decode_rollout_lineinordinal_state_for_rollout, and the same for the first-line parse inread_history_metadata.thread-store/src/local/model_context.rs: same change to the reverse scan that rebuilds model context — it currently silently drops float-bearing records for the same reason.With those two changes: the new test passes,
cargo test -p codex-rolloutis 124/124,cargo test -p codex-thread-storepasses,cargo clippy --tests -D warningsis clean. I know external PRs aren't accepted; the branch is here if it's useful to pull from: https://github.com/Ash20pk/codex/commit/01a6fd3e26fe50591b05336d34ca5477bf276e59 (branchfix/rollout-ordinal-resume-float-records)Two things the fix does not do
expected N, got N-1at the duplicate, so those threads need a one-off repair (renumber from the first duplicate onward, then drop the thread'sthread_history_projection_staterow so it re-materializes). That's a maintainer call.Other typed
RolloutLineparses with the same latent issue, lower stakes because they degrade rather than corrupt:rollout/src/list.rs:1231,1288(read_head_for_summaryskips the line) androllout/src/search.rs:251(content search misses it).Codex Desktop: paginated history becomes permanently truncated after interrupted turn/resume
Submission target
Independent Windows confirmation for:
codex.exe exits with code 0 mid-session; thread-store then fails "expected ordinal N, got N-1"Environment
10.0.26200)26.820.7780.0codex-cli 0.150.0-alpha.826.818.618090.149.0-alpha.4.10.149.0-alpha.4.3paginatedActual behavior
Three consecutive project threads developed the same failure. After a turn was interrupted without a durable
task_complete, later turns continued to be written to the canonical rollout JSONL. After reopening or resuming the thread, the Desktop UI displayed only the older projected portion. Newer user messages, assistant messages, tool activity, and completed turns remained present in the rollout but were absent fromthread/readand the conversation pane.Each affected projection contains one stale
inProgressturn. The Desktop resume log recognizes the latest turn asinterrupted, but the durable projection is not reconciled.Every subsequent write repeatedly logs:
Sanitized evidence
| Case | Runtime at creation | Projection expects | Boundary record | Boundary ordinal | Following record | Hidden rollout suffix | Projection warnings |
|---|---|---:|---|---:|---|---:|---:|
| 1 |
0.149.0-alpha.4.1|1002|thread_settings_applied|1001|task_started(1002)| 104.3 MiB | 361 || 2 |
0.149.0-alpha.4.3|3198|thread_settings_applied|3197|task_started(3198)| 10.9 MiB | 357 || 3 |
0.149.0-alpha.4.3|4490|thread_settings_applied|4489|task_started(4490)| 9.3 MiB and growing | 378 |In every case, the physical record immediately before the projection boundary is
event_msg/token_countwith the same ordinal later reused byevent_msg/thread_settings_applied:This leaves the projection checkpoint expecting
N, while the next unprojected durable record is the replayedN-1.The raw JSONL files parse successfully and retain the omitted conversation. SQLite integrity checks pass.
codex doctor --jsonreports both the state database and thread-history database as intact.codex migrate-rollouts --thread <id> --jsonreportsalready_paginatedand processes zero bytes, so it does not repair the projection.Updating and restarting Codex Desktop did not repair the three already-affected threads.
Reproduction sequence
task_completeorturn_aborted, leaving a trailingtoken_countrecord.token_countordinal forthread_settings_applied.Expected behavior
inProgressindefinitely.Privacy
No prompts, transcript text, repository names, local filesystem paths, account identifiers, credentials, raw databases, raw logs, or complete thread IDs are included. Complete local evidence can be retained for a private diagnostic channel if requested by OpenAI.
Additional Windows data point for the same failure family, with an OS-level crash signature.
Environment
26.820.7780.00.150.0-alpha.819045Crash and timing
On 2026-08-26 at 00:54:34 local time, Windows Error Reporting recorded an actual
codex.execrash:BEX640xc00004090x7codex.exeAt the time of the crash, that engine process was actively servicing a resumed archived thread with a 220.7 MB durable rollout. Backend requests immediately before the crash returned 200 OK.
After the app restarted, the affected thread emitted 184 repeated projection failures over about 10 minutes:
A second archived thread with a 631.1 MB rollout had previously emitted repeated
thread-store conflict: ... already has an active writererrors and retained three non-completed turn rows.Local integrity checks
PRAGMA quick_checkreturnedokfor:logs_2.sqlitethread_history_1.sqlitestate_5.sqlitequeue_1.sqliteThis supports a logical projection/index inconsistency rather than general SQLite corruption.
Recovery applied
I created a consistent SQLite backup, verified it with
quick_checkand SHA-256, then removed only the derived projection rows for the two affected thread IDs from:thread_history_projection_statethread_itemsthread_turnsThe durable rollout JSONL files were not modified or deleted. The transaction completed successfully, the live database still passes
quick_check, and no new crash or Windows Application Hang event appeared afterward. Full re-projection on a later resume/restart remains to be observed because intentionally reopening the 220 MB / 631 MB threads during diagnosis could re-trigger the UI stall.Resource context
At diagnosis time:
This may amplify UI stalls, but the ordinal mismatch and the same-second engine/renderer crash are the stronger evidence. Raw transcripts, private paths, thread IDs, and dumps are intentionally not attached; more diagnostics can be provided through a secure channel if needed.
Independent Windows confirmation: Desktop process replacement reused a
token_countordinal fortask_startedI reproduced the same failure family on a local paginated Codex Desktop thread. This adds a process-replacement trigger and a boundary variant where the duplicated record is
task_startedrather thanthread_settings_applied.Environment
26.825.3734.026.820.60940to26.820.71523paginatedDurable boundary
The interrupted Goal turn had no
task_completeorturn_aborted. Its final durable record was:After the Desktop/app-server process was replaced, the next continuation began with:
The stored projection checkpoint remained:
The record at that byte offset is the duplicated ordinal-4726
task_started. Every later projection therefore fails with:I counted 365 occurrences of this exact warning for the affected thread.
User-visible impact
inProgressturn.PRAGMA quick_checkreturnsokforstate_5.sqlite,thread_history_1.sqlite, andlogs_2.sqlite.This supports the root cause already described here: reverse ordinal discovery skips the valid floating-point
token_counttail, resumes fromN-1, and permanently wedges the derived projection. The new detail is that an automatic Desktop/app-server replacement during a Goal continuation can trigger it, and newer resume paths may reuse the ordinal directly fortask_startedwithout an interveningthread_settings_appliedrecord.No transcript text, repository name, credentials, local paths, raw databases, or complete thread ID are included. I can retain the full local evidence for a private diagnostic channel if needed.