openai / openai/codex

Codex desktop history/resume: duplicate rollout ordinals and remaining unknown-tail reuse

Open
#44,440 1 comment 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

What version of the Codex App are you using (From “About Codex” dialog)?

Desktop host: ChatGPT desktop app with the embedded Codex desktop experience, bundle version 26.901.51231, build 8109; bundled runtime: codex-cli 0.153.4. Version source: installed host bundle metadata, not a standalone Codex app's About dialog. codex-cli identifies the bundled runtime; the observed failure was in the desktop task window.

What subscription do you have?

ChatGPT Pro for the original affected desktop run, as recorded in its rate-limit event. The synthetic Rust regression requires no subscription, credentials, or model inference.

What platform is your computer?

macOS 15.5, Apple Silicon; uname -mprs: Darwin 24.5.0 arm64 arm.

What issue are you seeing?

Affected surface: Codex desktop app; area: task history, resume, and rollout persistence.

In the Codex desktop task window, reopening/resuming a long-running task exposed an older history prefix even though later work was still present in its raw persisted rollout. Task metadata continued to update, but the readable history did not catch up. From the user's perspective, recent task context appeared to be missing and had to be reconstructed manually.

The evidence establishes stale history projection/reading, not deletion of the raw conversation, a compaction failure, or the exact model input on later turns.

Evidence and scope
Layer What is established What is not claimed
Affected desktop task Distinct adjacent records share 4628; checkpoint/error match; later raw history remains present. A fresh UI reproduction on the latest desktop release.
Source after #42378 Unknown-type tails cause ordinal reuse; the synthetic regression fails before the patch. That an unknown event caused the original incident or current projection still stalls.
Reference patch Regression/edge cases and all 391 rollout/thread-store tests pass. Desktop deployment, end-to-end UI recovery, or repair of existing files.
Sanitized evidence from the affected desktop task

The relevant adjacent JSONL records and saved checkpoint were inspected locally:

Raw byte range (zero-based, end-exclusive) Ordinal Record
[30902383, 30903199) 4628 event_msg/token_count, including the literal used_percent: 24.0
[30903199, 30904760) 4628 thread_settings_applied
[30904760, 30905008) 4629 event_msg/task_started

The saved checkpoint was next_rollout_byte_offset=30903199, next_rollout_ordinal=4629: exactly the start of the second record with ordinal 4628. Subsequent projection attempts repeatedly reported:

thread history projection for <redacted-thread-id> expected ordinal 4629, got 4628

These are different events, not byte-identical duplicates. Later JSONL records remained beyond the stalled checkpoint; a newer metadata timestamp did not establish that readable history was current.

Relationship to existing reports and why a remaining defect is included

I searched #35746, #41566, #43182, and the discussion on #35746 before filing. The original desktop incident is consistent with the known decimal-decoder/duplicate-ordinal problem. #42378 already provides canonical decoding of known records with nested decimals; #42369 adds resilient projection. Those fixes are not new work in this report.

The new actionable result is unknown-tail ordinal reuse remaining after #42378. Its regression fails on a base containing both fixes, while the existing decimal regression passes. This is the scope of the attached patch, not proof of what triggered the original desktop incident.

If the desktop symptom belongs under an existing issue, please retain the distinct unknown-tail reproducer when triaging this report.

After submission, the duplicate checker also suggested #44432, #43451, and #44146. I reviewed their report bodies: they add desktop duplicate-ordinal/checkpoint evidence, including other affected runtime versions, but do not contain the post-#42378 unknown-type-tail synthetic reproducer included here. #44417 is my own superseded report and is now closed; this report consolidates the same investigation.

What steps can reproduce the bug?
A. Observed desktop workflow (not a clean-install reproducer)
  1. Continue working in an existing long-running Codex desktop task across multiple turns.
  2. Reopen/resume that same task in the desktop task window.
  3. Observe that the available task history stops at an older point rather than the latest persisted work.
  4. Compare read-only diagnostics: the task's raw JSONL contains later records, while the history projection repeats the ordinal-mismatch error and remains at the boundary shown above.

These steps describe the affected session's observed workflow. They are not sufficient by themselves to guarantee reproduction in a fresh desktop profile; the original triggering execution trace was not captured. Do not inject records into a real session or edit its database to try the regression below.

B. Deterministic source reproducer for the remaining defect

The source-only regression below was tested separately on ea53c8d4f78e3f2c9ae2bafcb387d677f33d8b6a, which already contains #42369 and #42378. At the 2026-09-10 check, official main was ddea03ad049142943bdbf13e937b1d67e8c1ba0c; its only additional commit changed Python SDK files, and the ordinal.rs / recorder_tests.rs Git blobs were identical to the tested base. No separate build/test run on ddea03ad or desktop rollout of the reference patch is claimed.

With persisted ordinals [0, 4, 5], where 5 belongs to an unknown outer record type or unknown event_msg subtype:

Expected after append: [0, 4, 5, 6]
Actual on tested base: [0, 4, 5, 5]

Both RolloutRecorder::new(...resume(path)) and append_rollout_item_to_path share the affected ordinal-discovery path. A sequential resume is enough; concurrency, a forced crash, or invalid JSON is not required.

The regression test itself uses only a temporary JSONL file and real recorder APIs. It does not read or modify an existing user session/database, and it makes no model requests.

  1. Create paginated session metadata at ordinal 0 and a known agent-message record at 4. The deliberate gap is legal and also covered by the repository's existing gap test.

  2. Append the following complete, newline-terminated JSON object at ordinal 5:

    {"timestamp":"2026-07-09T00:00:05Z","ordinal":5,"type":"event_msg","payload":{"type":"future_event"}}
    

    An unknown outer type such as "type":"future_record","payload":{} also reproduces the bug.

  3. Resume the recorder, append a known agent message, and shut it down normally.

  4. Inspect the appended line's ordinal. On the unmodified production code it is 5, not 6.

The executable test is resumed_paginated_rollout_preserves_unknown_tail_ordinal. It also checks that the previous file contents are preserved and that the appended message is correct.

Commands to run the failing regression and then the reference fix in a fresh checkout

Prerequisites: the repository's Rust toolchain, git, just, cargo-nextest, and uv. Run in a fresh temporary directory; the commands below never target an existing Codex data directory. Dependency downloads may require internet access; the regression itself does not call a model service.

repro_dir=$(mktemp -d)
git clone --filter=blob:none https://github.com/singularityDLW/codex.git "$repro_dir/codex"
cd "$repro_dir/codex"
git switch --detach ea53c8d4f78e3f2c9ae2bafcb387d677f33d8b6a

# Add only the tests, leaving production ordinal discovery unchanged.
git restore --source=049a6e225ce3a19a5e575c475fe47445cb2424e7 -- codex-rs/rollout/src/recorder_tests.rs
cd codex-rs
CARGO_INCREMENTAL=0 CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 \
  CARGO_BUILD_JOBS=2 uv run --no-project just test -p codex-rollout \
  resumed_paginated_rollout_preserves_unknown_tail_ordinal

The last command is expected to fail with Some(5) versus Some(6). Then, from the same codex-rs directory:

cd ..
git restore --source=049a6e225ce3a19a5e575c475fe47445cb2424e7 -- codex-rs/rollout/src/ordinal.rs
cd codex-rs
CARGO_INCREMENTAL=0 CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 \
  CARGO_BUILD_JOBS=2 uv run --no-project just test \
  -p codex-rollout -p codex-thread-store

The expected fixed result is 391 passed, 0 skipped. This reproduces the test-only/production-change separation used in my validation; the fresh-clone wrapper above was not independently rerun after the build cache was cleaned.

What is the expected behavior?
  • Desktop behavior: reopening/resuming a task should expose its latest recoverable persisted history. If a damaged record prevents recovery, the app should surface the problem rather than silently presenting an older prefix as current.
  • Persistence invariant: a durable ordinal in a valid JSON object remains occupied even if this version cannot interpret its payload. Both recorder resume and one-shot append must allocate after it.
  • Existing bytes and ordinal identities should remain unchanged. Invalid JSON/non-object tails must not be treated as rollout envelopes; missing ordinal and overflow must be handled explicitly. Existing legacy and subagent-prefix protections must remain intact.

The first point is the desired user-facing outcome; the reference patch addresses only the ordinal-allocation invariant. End-to-end desktop recovery and repair of pre-existing files have not been validated.

Additional information
Test environment

Tests used the repository's Rust 1.95.0 toolchain. Other operating systems were not tested locally.

Root-cause analysis: observed boundary versus inferred historical trigger

Historical trigger inference: the older flattened decoder can reject certain nested decimals, skip a persisted tail record during resume, and allocate its ordinal again. The original 24.0 record and the upstream decimal fix support that mechanism, but there is no captured execution trace proving that precise decoder branch ran during the original incident.

Separately reproduced residual defect:

In ordinal_state_for_rollout, the reverse scan still requests a fully decoded RolloutLine via scan_next_rollout_line(). An unknown payload is rejected, so scanning continues to an earlier known record. ordinal.checked_add(1) then allocates an already occupied position.

#42378 fixed how known records with nested decimals are decoded. It did not separate durable ordinal discovery from recognition of every possible payload type. That is the distinction this reproduction exercises.

#42369 makes current projection more resilient; the remaining ordinal collision is independently incorrect but does not prove a current desktop stall.

Reference patch, supplied through the issue workflow

Following the contribution guide, I am supplying the analysis, reproducer, and patch as issue material rather than requesting acceptance of an external code PR:

Only two files change: rollout/src/ordinal.rs and its existing recorder test file. No dependencies, protocol schemas, SQLite migrations, or history-projection policies change.

The patch first requires a JSON map, then decodes an ordinal-only envelope. Requiring an object matters: Serde's default derived struct deserializer also accepts positional arrays, so [4] must not become a fake ordinal envelope. Independent review caught that in the first draft; a failing regression was added before the final object-only fix.

Scope and deliberate edge behavior:

  • Unknown outer/inner event types still reserve their ordinals.
  • Complete, unterminated, and malformed suffixes are covered without rewriting previous bytes.
  • Arrays, scalars, and null are not rollout envelopes and are skipped.
  • An unknown record at u64::MAX exhausts ordinal space instead of allowing reuse.
  • A complete final object without an ordinal fails append conservatively. This extends the existing missing-ordinal check to unknown records and is explicitly tested.
  • The patch does not renumber damaged logs, reconstruct all conflicting historical records, or update a desktop installation. Recovery of pre-existing damage is distinct from preventing a new collision.
Verification performed
Check Result
New unknown-tail regression against unchanged production code Failed: actual Some(5), expected Some(6); reproduced on retry
Existing decimal-token-count regression on the same base Passed
Unknown-tail regression after the implementation change Passed
Non-object-tail regression against the first draft Failed for [4]; fixed by object-only decoding
Final just test -p codex-rollout -p codex-thread-store 391 passed, 0 skipped
just fmt Passed
just clippy -p codex-rollout -- -D warnings Passed
git diff --check Passed

The full-module run includes existing decimal parsing and malformed/duplicate projection recovery tests. No full-workspace or cross-platform test run is claimed. Final correctness re-review found no remaining issue with the object-only fix.

Runtime diagnostics and privacy

The regression makes no model requests. Historical model/context-window metrics are not included in this report and are not inferred from the history cutoff.

Diagnostics/tests used a non-interactive command runner. TERM=dumb explains the terminal diagnostic failure; no TUI is involved.

Ran the installed desktop-bundled codex doctor --json on 2026-09-10. The report describes that installed 0.153.4 runtime, not the compiled regression-test binary.

The complete set of check statuses and summaries is included below. Private paths, original thread IDs, integration inventory, and unrelated local configuration details are omitted. This is explicitly a privacy-minimized summary, not the unmodified JSON output. Its warnings/failure are not hidden and are not evidence that the synthetic bug depends on those environment conditions.

Privacy-minimized doctor output: all 23 check statuses and summaries
{
  "schemaVersion": 1,
  "generatedAt": "1789015336s since unix epoch",
  "overallStatus": "fail",
  "codexVersion": "0.153.4",
  "redactionNote": "Every check status and summary is retained. Details, issues, remediation, and timing fields are omitted to avoid publishing private paths, original thread IDs, integration inventory, and unrelated local configuration. This is a privacy-minimized summary, not the unmodified doctor JSON.",
  "checks": {
    "app_server.status": {
      "status": "warning",
      "summary": "background server socket is stale or unreachable"
    },
    "auth.credentials": {
      "status": "ok",
      "summary": "auth is configured"
    },
    "config.load": {
      "status": "ok",
      "summary": "config loaded"
    },
    "desktop.app.version": {
      "status": "ok",
      "summary": "the desktop application is installed"
    },
    "desktop.app_server.handshake": {
      "status": "ok",
      "summary": "no desktop app-server handshake was recorded"
    },
    "desktop.security.enforcement": {
      "status": "warning",
      "summary": "the desktop security assessment was unavailable"
    },
    "git.environment": {
      "status": "ok",
      "summary": "git version 2.39.5 (Apple Git-154)"
    },
    "installation": {
      "status": "ok",
      "summary": "installation looks consistent"
    },
    "mcp.config": {
      "status": "ok",
      "summary": "MCP configuration is locally consistent"
    },
    "network.env": {
      "status": "ok",
      "summary": "network-related environment looks readable"
    },
    "network.provider_reachability": {
      "status": "warning",
      "summary": "desktop update and runtime CDN is unreachable"
    },
    "network.websocket_reachability": {
      "status": "ok",
      "summary": "Responses WebSocket handshake succeeded"
    },
    "runtime.provenance": {
      "status": "ok",
      "summary": "running local build on macos-aarch64"
    },
    "runtime.search": {
      "status": "ok",
      "summary": "search is OK (system)"
    },
    "sandbox.helpers": {
      "status": "ok",
      "summary": "sandbox configuration is readable"
    },
    "security.endpoint": {
      "status": "ok",
      "summary": "no supported endpoint protection detected"
    },
    "state.paths": {
      "status": "ok",
      "summary": "state paths and databases are inspectable"
    },
    "state.rollout_db_parity": {
      "status": "warning",
      "summary": "rollout files and state DB thread inventory differ"
    },
    "system.disk": {
      "status": "ok",
      "summary": "sufficient free disk space (5.7 GiB)"
    },
    "system.environment": {
      "status": "ok",
      "summary": "OS language zh-Hans-CN"
    },
    "terminal.env": {
      "status": "fail",
      "summary": "TERM=dumb - colors and cursor control are disabled"
    },
    "terminal.title": {
      "status": "ok",
      "summary": "terminal title default"
    },
    "updates.status": {
      "status": "ok",
      "summary": "update configuration is locally consistent"
    }
  }
}

Original conversations, task identifiers, workspace names, credentials, databases, and full production logs are intentionally not attached. The byte offsets, event types, and redacted error above provide the relevant boundary evidence; executable regression fixtures are synthetic.

Requested triage

Replacement report: this App-form submission replaces my earlier report #44417 and contains the same investigation, not a second independent incident. The earlier report is now closed as superseded in favor of this one. The observed product impact is in the Codex desktop app; the reproducible residual defect is in session/rollout persistence, with the verification limits stated above.

Could the team evaluate the remaining unknown-tail defect and advise which desktop build includes the existing projection/decoder fixes? The patch is reference material under the contribution guide, not an external code PR.

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, especially ordinal_state_for_rollout, and the regression in codex-rs/rollout/src/recorder_tests.rs. Run resumed_paginated_rollout_preserves_unknown_tail_ordinal to reproduce the reused ordinal, then run the codex-rollout and codex-thread-store tests. Done means unknown valid records retain their ordinal and the referenced test suite passes without changing existing bytes.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.