openai / openai/codex

[0.153.4][Windows] Resume reuses two rollout ordinals after trailing token_count records, freezing desktop history

Open
#43,142 21 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

app bug session windows-os
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

Summary

After resuming an interrupted local task, Codex Desktop repeatedly shows an older conversation state when refreshed or reopened. Later user messages and the completed assistant response are still present in the durable rollout JSONL, but the paginated history projection no longer advances.

This report adds a two-record ordinal regression on bundled CLI 0.153.4 and a specific suspected deserialization path to the existing duplicate-ordinal reports.

Environment

  • Codex Desktop Windows package: 26.901.6511.0
  • Bundled codex-cli --version: 0.153.4
  • Windows 11 Pro x64, build 10.0.26200
  • Affected rollout: history_mode = paginated, recorded CLI version 0.153.4

Verified on-disk failure boundary

The following are actual top-level ordinals, with conversation contents and identifiers omitted. Times are September 5, 2026, UTC+08:00.

Physical JSONL line Time Record Stored ordinal
365 21:00:26.532 tool result 8288
366 21:00:26.543 event_msg/token_count 8289
367 21:00:33.774 event_msg/token_count 8290
368 21:05:01.561 event_msg/thread_settings_applied 8289 (reused)
369 21:05:01.573 event_msg/task_started 8290 (reused)

The inspected segment contains exactly one backward ordinal transition. All lines are valid JSON. Both trailing token_count records contain a rate-limit field used_percent: 82.0.

The segment's projection checkpoint remains:

next_rollout_byte_offset = 2007813
next_rollout_ordinal = 8291

Byte offset 2007813 points exactly to physical line 368. Every subsequent append encounters:

codex_thread_store::local::live_writer:
failed to project durable rollout for <thread>:
thread-store internal error:
thread history projection for <segment> expected ordinal 8291, got 8289

Logs show thread/resume at 21:02:40, then resume_interrupted_task and the first projection error at 21:05:01. The history-reading interface still returns the earlier turn as inProgress; later task_complete and user-message records are present in the JSONL. Continuing the task appends more durable records without updating the rendered history.

Suspected implementation cause in rust-v0.153.4

This is a source-level diagnosis consistent with the on-disk evidence, not an independently compiled Rust reproduction:

  1. ordinal_state_for_rollout scans backward as RolloutLine, silently skips rejected records, and sets the next ordinal from the first successfully deserialized record.
  2. ReverseJsonlScanner::finish_record uses ordinary serde_json::from_slice.
  3. The same version already has decode_rollout_line, with an explicit workaround for floating-point deserialization through flattened envelopes under serde_json/arbitrary_precision. The ordinal scan bypasses that decoder.

If the two trailing rate-limit records are rejected by the ordinary deserializer, the backward scan falls back to ordinal 8288 and resumes at 8289. This predicts the exact two-record regression observed above. There is no evidence establishing concurrent writers as the cause in this case.

Observed reproduction and impact

  1. Continue a long-running local task with paginated history.
  2. Resume an unfinished/interrupted turn whose durable tail contains the two rate-limit records above.
  3. Continue working, then refresh/reopen the task.
  4. The UI repeatedly returns to the old turn, although the newer conversation and completed work remain in the raw transcript.

A fresh deterministic reproduction has not been run. The captured resume boundary and repeated history failure are directly observed. This can cause users to repeat already completed work because the displayed conversation appears to have reverted.

Expected behavior / suggested investigation

  • Recover the next ordinal using the same compatible decoder as normal rollout loading, without silently moving backward past valid durable records.
  • Add regression coverage for resuming a paginated rollout ending in multiple token_count records with floating-point rate-limit values.
  • Surface an actionable history error and provide a supported per-thread recovery path for already affected transcripts.

Related: #42027 and #41657 (reused ordinals after resume), #42794 (floating-point decoding failures). Investigation was read-only; no transcript or database repair was attempted. Raw conversations, credentials, thread identifiers, and personal paths are not attached.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with codex-rs/rollout/src/ordinal.rs and compare its backward scan with the compatible decoder in codex-rs/rollout/src/lib.rs; inspect ReverseJsonlScanner::finish_record in reverse_jsonl_scanner.rs as the deserialization boundary. Add regression coverage for a paginated rollout ending in multiple token_count records with floating-point rate-limit values. Done means resume preserves the next ordinal and history continues projecting instead of freezing.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.