openai / openai/codex

[Bug] Competing paginated rollout writers emit overlapping ordinal sequences

Open
#41,353 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Summary

Paginated rollout JSONL can receive overlapping ordinal sequences from competing/stale writer state. A single duplicate or backward ordinal then permanently wedges thread_history_projection_state, leaving the UI on an old snapshot while the canonical transcript continues growing.

This is broader than a one-off corrupted cursor: multiple active threads can become affected simultaneously, and one rollout can contain several independent ordinal regressions.

Related symptom report: #41079.

Environment
  • Codex Desktop on Windows 11 x64
  • Paginated thread history
  • Observed with a current bundled alpha runtime
Sanitized fleet evidence
  • All SQLite databases passed PRAGMA quick_check, foreign_key_check, and migration checks.
  • Four live threads were wedged concurrently.
  • Logs contained more than 1,200 repeated failed to project durable rollout warnings.
  • Ten non-monotonic ordinal transitions were found across the 43 rollouts represented in thread_history_projection_state.
  • One affected rollout contained three separate regressions, so advancing the SQLite cursor past only the first duplicate would fail again later.
Producer pattern

A representative sequence was:

ordinal N:   event_msg/token_count
ordinal N+1: event_msg/token_count
ordinal N:   event_msg/thread_settings_applied
ordinal N+1: event_msg/thread_goal_updated
ordinal N+2: event_msg/task_started
ordinal N+3: later durable history continues normally

This looks like two append paths assigning ordinals from independently cached tail state. The newer turn starts from stale N even though another writer has already persisted N and N+1.

Projection failure

read_projection_steps currently treats every ordinal < next_ordinal as fatal. Consequently, even stale records that produce no projected turns/items permanently block all later valid history:

thread history projection expected ordinal N+2, got N

The durable rollout remains complete and parseable. The SQLite view simply stops advancing.

Expected behavior
  1. All writes to one rollout must share one serialized ordinal allocator, or refresh and lock the durable tail before assigning ordinals.
  2. A stale decoded record that is provably a thread-history no-op must not permanently wedge projection.
  3. Meaningful duplicate or out-of-order records must still fail closed or trigger a safe rebuild rather than being silently discarded.
  4. Projection failure should be surfaced in the UI and have an automatic repair path.
Tested defensive patch

Patch and regression test:

https://github.com/TryDotAtwo/codex/commit/c148c15fb7e75cd76865f75d813cb249cde40f6c

The patch skips a stale ordinal only when:

  • the line decoded successfully;
  • no rejected lines precede it;
  • project_rollout_line produces no changed turns or items;
  • it is not a projected realtime item.

Meaningful duplicate ordinals remain rejected by the existing test.

Verification:

cargo test -p codex-thread-store
235 passed; 0 failed

The regression test was verified red before the implementation (expected ordinal 2, got 1) and green afterward.

Privacy

No thread identifiers, prompts, absolute paths, credentials, raw logs, or user content are included.

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 in the codex-thread-store package by reading read_projection_steps and project_rollout_line, then run the package tests. Use the documented regression scenario to distinguish stale no-op records from meaningful duplicate or out-of-order records. Done means projection no longer wedges on provably harmless stale records while meaningful ordinal failures remain rejected and the regression test passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, sqlite
Domain
backend, databases
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.