[macOS Desktop] Reboot can switch app-server runtime and wedge paginated history at a duplicate ordinal

Open 💬 9 comments Opened Aug 23, 2026 by GravityPoet
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Environment

  • OS: Darwin 27.0.0 arm64 arm (macOS)
  • Codex Desktop: 26.818.41509 (build 6962)
  • Bundled CLI: codex-cli 0.149.0-alpha.4.1
  • Local Desktop thread history mode: paginated

Summary

After a normal macOS restart, Codex Desktop can reopen a paginated thread with an older
conversation snapshot, stop responding, or fail to recover it. The canonical rollout JSONL
still contains the newer turns, but the Desktop history projection stops advancing and the UI
serves stale history.

This is not limited to a single missing file. The observed failure is a restart/login bootstrap
race: the app-server runtime selected before reboot is not guaranteed to be the runtime selected
after reboot. When the second runtime resumes the same paginated rollout, it can write a record
with an ordinal already used by the previous runtime. The projection then waits for the next
ordinal forever.

Observed errors

Depending on which action is attempted, the Desktop UI shows variants of:

  • failed to resolve rollout path ... file does not exist
  • no rollout found for thread id ...
  • invalid paginated history lineage ... missing source rollout
  • 恢复对话失败 (conversation recovery failed)

In another affected state, the thread remains on “thinking” without producing a response, and
retrying reports that the session is already open in another application.

Reproduction timeline

  1. Start a long-running local paginated Desktop thread and let it write multiple turns.
  2. Leave the thread's rollout and state databases intact.
  3. Restart macOS normally.
  4. Launch Codex Desktop and open/continue the same thread.
  5. Observe that the Desktop app-server may be launched from a different runtime than before

reboot. The resumed rollout can contain a duplicate ordinal (for example, a valid record at
ordinal N before reboot and a different thread_settings_applied record at the same ordinal
N after reboot).

  1. The projection cursor remains at the earlier boundary (expected N+1), while the raw rollout

continues to grow. Opening the thread shows older history and follow-up/retry may fail.

The same class of failure is likely when switching accounts or otherwise rebuilding the GUI
session, although the clean-restart path above is the independently verified reproduction.

Evidence from one affected thread

  • The raw rollout remained newline-valid and sequential through the latest completed turn after

recovery; no conversation content was deleted.

  • Before reboot, the patched app-server wrote a valid token_count record at ordinal 3588.
  • After reboot, the official Desktop app-server wrote thread_settings_applied at ordinal 3588

in the same logical thread.

  • The SQLite projection then stopped at the duplicate boundary and the UI remained behind the

raw rollout.

  • Reinstalling or reopening the app did not reconcile the projection automatically.

All identifiers, absolute paths, credentials, and conversation content are intentionally omitted.

Expected behavior

  • Desktop should select a deterministic, version-compatible app-server runtime across login,

reboot, and GUI-session reconstruction; a later process must not append duplicate paginated
ordinals to an existing rollout.

  • On startup and thread open, thread_history_* should be treated as a rebuildable projection:

detect ordinal/byte-offset mismatches, reconcile it from the canonical rollout, and advance it
to the durable end instead of silently freezing.

  • If a source rollout truly is missing, recovery should report a precise actionable error and

avoid leaving a ghost/stale thread that appears present but cannot continue.

  • A follow-up should either be durably persisted and start a response, or return an explicit

dispatch failure; it must not appear successful while leaving the thread unchanged.

Impact

Users see several completed rounds disappear from the visible conversation, cannot continue the
latest turn, and may retry against an old context. This is especially damaging for long-running
tasks because the durable record and the UI disagree without a clear recovery path.

Related reports

  • #35746 — paginated history drops valid rollout records and reuses ordinals
  • #38792 — desynchronized projection cursors are never repaired on resume
  • #40112 — Desktop shows stale pre-crash state after restart while durable history is intact
  • #40109 — concurrent rollout writers can assign duplicate paginated ordinals

Please consider a startup reconciliation/doctor path and a regression test that exercises a normal
macOS reboot (or GUI-session rebuild) before and after resuming the same paginated thread.

View original on GitHub ↗

9 Comments

github-actions[bot] contributor · 5 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #40109
  • #40112
  • #38792

Powered by Codex Action

pomazanbohdan · 5 days ago

Cross-OS confirmation: the same duplicate-ordinal boundary appears in Windows history after restart (token_count followed by thread_settings_applied at N). The canonical JSONL remains intact while the paginated projection wedges, so repair needs durable-rollout preservation plus a thread_history cursor parity check. Related: #35746, #40109, and #38792.

hugepaper · 5 days ago

Windows confirmation of the same duplicate-ordinal boundary, observed after an authentication/session rebuild.

Environment

User-visible impact

After the repeated refresh_token_invalidated sign-in loop, quitting and reopening Codex Desktop causes previously visible conversation history to disappear from the UI. During a running process, the affected long thread can continue receiving new turns, but the durable history projection does not advance.

Read-only validation: the conversations were not deleted

A read-only inspection found:

  • 63 user-created local threads; 0 archived
  • all 63 remain present in the state database, session index, local display catalog, and their rollout files
  • all 63 catalog rows have missing_candidate = 0
  • SQLite integrity checks returned ok
  • no missing rollout file was found for any of the 63 user threads

There is also a catalog/selection inconsistency after reauthentication: the globally selected remote-host value matches 0 of the 2 hosts registered in the local catalog, while 33 legacy global project-thread assignments point to the selected project but all 63 current user-thread/catalog rows have a null project_id. This may help explain the empty sidebar, but it is reported as an observation rather than a proven cause.

Exact duplicate-ordinal evidence from the affected paginated thread

At a read-only snapshot:

  • canonical rollout size: 105,667,652 bytes
  • JSONL records: 11,645
  • invalid JSON records: 0
  • last canonical ordinal: 11643
  • duplicate ordinals found: exactly 1
  • ordinal 591 occurs exactly twice, consecutively:
  1. event_msg / token_count, ordinal 591
  2. event_msg / thread_settings_applied, ordinal 591
  • stored projection cursor:
  • next_rollout_byte_offset = 6502760
  • next_rollout_ordinal = 592
  • the record at that byte offset is the second ordinal 591
  • materialized history contains only 183 items and 5 turns, while the canonical rollout continues through ordinal 11643

The live writer repeatedly reports:

failed to project durable rollout: thread-store internal error:
thread history projection expected ordinal 592, got 591

This same error was recorded at least 542 times across 3 distinct Desktop/app-server processes, so reopening the app does not reconcile or repair the projection.

The first projection failure began on 2026-08-22 at 21:10 KST, after the 20:28–20:38 KST authentication loop. That establishes temporal correlation only; it does not prove authentication caused the duplicate ordinal.

Expected behavior

  • A GUI-session rebuild, reauthentication, or restart must not append a duplicate paginated ordinal.
  • On a cursor/ordinal mismatch, Desktop should rebuild the derived history projection from the intact canonical rollout.
  • Reauthentication or catalog-host changes should not make intact local conversations disappear from the sidebar.

No identifiers, credentials, conversation text, filenames, absolute paths, email addresses, request IDs, account IDs, thread IDs, or raw log/database files are included.

hugepaper · 5 days ago

Additional read-only follow-up narrows both the likely mechanism and the scope of the Windows instance reported above.

Exact #35746 fingerprint and handoff timeline

The first ordinal-591 record is not merely a generic token_count: its rate_limits, nested primary, and credits fields are all populated structured maps (values intentionally omitted). This exactly matches the resume-tail record fingerprint described in #35746.

2026-08-22 11:32:13.016Z  event_msg/token_count(591)
2026-08-22 11:47:59.759Z  event_msg/thread_settings_applied(591)
2026-08-22 11:47:59.768Z  event_msg/task_started(592)

The duplicate appears 15m46.743s after the structured token_count. Records immediately before and after this boundary increase normally.

Process attribution cannot be proven from the JSONL because rollout records do not carry a process UUID. The logs nevertheless show a strong handoff signal: the process active for the first token_count stopped logging, a different app-server process issued thread/resume about 5m52s before the duplicate, and only the newer process overlaps the second record's time window. This is consistent with a cross-process resume, but it is not proof that two writers appended concurrently.

The last authentication-manager 401 refresh_token_invalidated occurred about 9m30s before the duplicate. The auth error preceded the observed resume/handoff boundary; the logs do not establish that it caused the handoff or ordinal reuse. The first projection failure surfaced later and therefore did not create the already-present duplicate.

Local-inventory negative control

At the initial full-scan snapshot, I parsed all 47 available user-created uncompressed rollout JSONL files, totaling 134,967 physical records:

  • invalid JSON, invalid UTF-8, blank lines, and non-object records: 0
  • 40 legacy-schema rollouts without ordinals
  • 7 ordinal-schema rollouts: 6 normal, 1 affected
  • backward ordinals: 0
  • skipped ordinal values: 0
  • duplicate ordinals: exactly one pair, the 591 boundary above

The cursor reported above lands at the exact start of the second 591. In a later snapshot ending immediately after ordinal 11,998 (record timestamp 2026-08-23 10:04:10.214Z), the derived projection was behind by the replayed 591 plus 11,407 later ordinal values (592 through 11,998): 11,408 unprojected physical records and 100,709,536 bytes.

This single duplicated ordinal explains the permanently stale projection for one long paginated thread. It cannot by itself explain why all 63 intact local threads disappeared from the Desktop sidebar after reauthentication. The selected-host/project catalog mismatch reported above remains a separate unresolved sidebar/catalog observation; causality is not established.

No field values, credentials, identifiers, paths, conversation content, repository data, or raw files are included.

JaredTheHammer · 3 days ago

Additional cross-platform evidence from Windows Codex Desktop 26.818.8289.0, bundled app-server 0.149.0-alpha.4.3.

The same paginated projection failure occurred during app-server reconnect: the cursor expected the next ordinal but read a duplicate of the previous ordinal from the canonical JSONL. I found this in 12 local rollouts. A backup-first repair advanced only projection byte offsets past the duplicate metadata records, preserved all raw rollouts, passed SQLite integrity checks, and restored a clean app-server reconnect.

This confirms the startup reconciliation behavior described here is also needed on Windows, and that a rebuildable projection should recover automatically instead of terminating the app-server with 0xFFFFFFFF.

wox0008 · 2 days ago

Additional macOS reproduction on a newer build; sequential resume narrows the likely seam

This is a sanitized corroborating observation for #40178, not a separate failure class.

Environment
  • macOS 26.5, arm64
  • Codex Desktop 26.818.61809 (build 7019)
  • bundled CLI 0.149.0-alpha.4.3
  • affected history mode: paginated
Observed sequence
  1. Before the new Desktop process started, the durable rollout ended with a structured event_msg/token_count record at ordinal N. A type-only inspection confirmed populated rate_limits data and a numeric primary.used_percent; all values are omitted.
  2. The new bundled app-server initialized once. About forty seconds later, thread/resume completed for the affected paginated thread.
  3. About one second after that resume, the same rollout received event_msg/thread_settings_applied at ordinal N, followed immediately by event_msg/task_started at N+1.
  4. A local structural scan parsed every JSONL record successfully and found exactly one duplicate ordinal: this N boundary.
  5. The history projector later failed with the sanitized signature expected ordinal N+1, got N; newer durable records remained beyond that boundary while the visible projection stayed stale.

I did not find evidence that proves two writers overlapped in this reproduction. The timing is consistent with a sequential process handoff/resume, so concurrent append alone may not be necessary to trigger the fault.

Source-level hypothesis, not a claim about the installed binary

In current public source at commit 7c6eb0e:

  • the local thread-store serializes per-thread operations with a process-scoped mutex and retains an OS writer lock for the recorder lifetime: live_writer.rs and mod.rs;
  • resume-tail ordinal discovery still uses scan_next::<RolloutLine>(): ordinal.rs;
  • the projector rejects any physical ordinal below its stored next_ordinal: thread_history_materialization.rs;

This makes the #35746 tail-decoding path the leading source-level hypothesis: if a valid top-level ordinal exists but the full trailing payload is skipped during resume-tail decoding, the resumed recorder can seed from an earlier record and reuse N. The structured rate-limit fingerprint strengthens that match, but this source inspection still does not prove that the installed Desktop binary executed that exact path.

Requested OpenAI guidance
  1. Is the tail-decoding interpretation above consistent with the bundled 0.149.0-alpha.4.3 implementation?
  2. Is there a supported doctor or repair path for an already-written different-content ordinal collision that preserves the durable rollout, and should the normal projector fail closed rather than skip either record?
  3. Until a fixed build is available, what is the safest supported user action after seeing this signature: stop using the affected thread, export diagnostics through in-app feedback, or another documented procedure?

Suggested regression: a structured token_count(N) tail followed by a cold sequential resume must append at N+1; a different-content collision must fail closed without discarding the last readable projection.

Related: #35746, #38792, and #40109.

Claim boundary: this is one sanitized corroborating observation plus public-source analysis. It does not prove universal scope, data deletion, the exact installed-binary control flow, or that any proposed fix is already available.

Disclosure: this comment was prepared with Codex assistance at the account owner's request. No raw history, identifiers, local paths, credentials, or conversation content are included.

dfg5467 · 2 days ago

Follow-up: repeated macOS reproduction and a failed self-hosted recovery path

No conversation content, full thread identifiers, absolute local paths, credentials, or account information are included below.

Environment:

  • ChatGPT/Codex Desktop: 26.820.60940 (build 7119)
  • Bundled app-server: 0.150.0-alpha.8
  • The duplicate-ordinal boundaries were originally written by Desktop 26.818.61809 / app-server 0.149.0-alpha.4.3
  • macOS arm64; paginated local history

Read-only audit:

  • The initial audit found 5 affected rollouts out of 77 paginated rollouts. Three diagnostic subagent rollouts were created during investigation, so the execution-time inventory became 5 affected out of 80.
  • The affected set is 3 user threads and 2 depth-1 subagent threads.
  • 23 completed turns are durable in JSONL but absent from the SQLite projection: 21 user-thread turns and 2 subagent turns.
  • All affected JSONL records parse, all files end on complete newline-terminated records, and SQLite integrity checks pass.
  • Every affected projection cursor stops exactly at the first resumed record whose ordinal regressed or duplicated the preceding ordinal.
  • Across the five threads, the live writer emitted 1,078 repeated projection warnings with the expected ordinal N, got N-1/N-2 signature.
  • Desktop restart reconstructs the last projected turn as interrupted and serves that stale projection, even though later final answers and task-complete events remain in the raw rollout.

Current-build behavior:

  • New records written by 26.820 / 0.150 have remained ordinal-contiguous in the observed samples.
  • The current build does not reconcile any of the five pre-existing 0.149 boundaries. Reopening, reading, resuming, reinstalling, and migrate-rollouts do not advance the frozen cursors.

Recovery-path finding:

An agent running inside the Desktop app cannot reliably perform the required offline recovery of the same app. Closing Desktop interrupts the hosting agent and its app-server; attempts to hand the repair to a detached local helper did not obtain a stable offline interval and were stopped without modifying any rollout or projection database. This is an architectural dead end for ordinary users, not a viable support procedure.

The product therefore needs a supported recovery path that does not depend on an agent modifying its own live storage:

  1. At startup/read/resume, compare each paginated projection cursor with the canonical rollout record at that byte offset and the rollout EOF.
  2. If an overlap, duplicate ordinal, regressing ordinal, or stale cursor is detected, rebuild that thread's derived thread_items, thread_turns, and projection state transactionally from the durable rollout.
  3. Preserve the old readable projection until the replacement passes integrity and semantic checks.
  4. Expose the same operation as a supported codex doctor --repair-history or equivalent UI action.
  5. Surface a synchronization error instead of silently showing an older conversation.
  6. Add regression coverage for normal macOS reboot, unfinished-turn shutdown, app-server runtime replacement, duplicate thread_settings_applied, and subagent rollouts.

The local canonical records remain preserved for a private diagnostic channel if maintainers request specific sanitized fields. No public raw transcript or database upload will be made.

XiaoTianFan · 1 day ago

Windows appendix: isolated recovery reaches the complete durable history

This is a sanitized corroborating case for the same failure class, with an isolated recovery validation. No project/repository name, local path, thread ID, title, prompt, response text, credential, account identifier, or raw database/log file is included.

Environment and impact
  • Windows x64
  • Codex Desktop 26.820.7780.0
  • Current bundled app-server/CLI 0.150.0-alpha.8
  • The affected boundary was written under 0.149.0-alpha.4.1
  • History mode: paginated

The durable rollout continued receiving valid turns through the current day, while Desktop remained frozen at the history visible around the duplicate boundary. Restarting Desktop did not change the visible cutoff.

Read-only evidence
  • Durable rollout: 234,948,420 bytes and 21,326 newline-delimited JSON records
  • JSON parse failures: 0
  • Ordinal anomalies: exactly one
  • Boundary shape:
  1. event_msg/token_count(N)
  2. event_msg/thread_settings_applied(N)
  3. event_msg/task_started(N+1)
  • All records after that pair are strictly sequential through the end of the file.
  • The stored projection cursor points to the exact beginning of the second N record while expecting N+1.
  • Live materialized history: 19 turns / 2,149 items
  • Canonical history represented by the complete rollout: 78 turns / 7,297 items
  • SQLite integrity check: ok
  • Foreign-key violations: 0
  • The writer logged the same expected ordinal N+1, got N projection failure 435 times; each later append retried and failed at the unchanged boundary.

This confirms that the later history was not deleted. The user-visible truncation is the stale derived projection.

Isolated recovery validation

I made private local copies using a SQLite online backup and a byte-for-byte rollout copy. In the copies only, I conditionally advanced the affected projection byte offset past the duplicate thread_settings_applied(N) record while retaining the expected next ordinal N+1. The durable rollout itself was not edited.

After starting the current 0.150.0-alpha.8 app-server and resuming the copied thread:

  • projection advanced to the exact rollout EOF;
  • all 78 turns / 7,297 items materialized;
  • the newest completed turn was returned by thread/turns/list;
  • SQLite integrity remained ok;
  • foreign-key violations remained 0.

The live installation was not modified.

This specific skip is narrow: thread_settings_applied produces no projected turn/item change in the current projection mapping, and the isolated replay confirmed no loss of later materialized history. A generic “delete and rebuild projection rows” repair would still encounter the duplicate ordinal unless rebuild/reconciliation explicitly handles the collision.

Product implication

The affected thread is recoverable, but ordinary users need a supported offline/startup repair path. A safe implementation could detect when the cursor lands on a duplicate projection-neutral metadata record, preserve the canonical rollout and old readable projection, rebuild or advance transactionally, validate the replacement, and only then publish it. The UI should also surface projection lag instead of presenting the stale history as complete.

Disclosure: this appendix was prepared and submitted with Codex assistance at the account owner's request.

evolsb · 16 hours ago

Sanitized macOS Remote confirmation and recovery result from a MacBook Pro host controlled through another Mac.

Environment:

  • Codex Desktop 26.820 generation
  • macOS arm64 host
  • paginated thread history
  • thread was actively continuing in a Schoolwise worktree while the local Desktop view stayed on a spinner or stale first turn

Exact boundary:

  • canonical JSONL remained parseable and append-only
  • token_count at ordinal N was followed after resume by thread_settings_applied at the same ordinal N
  • projection state expected N+1 at the byte offset where the duplicate thread_settings_applied record began
  • the next physical line was task_started at N+1
  • current OpenAI projection source maps thread_settings_applied to an empty ThreadHistoryChangeSet

Backup-first recovery:

  • took a SQLite online backup and verified integrity plus foreign keys
  • saved and hashed the immutable rollout prefix through the duplicate record
  • transactionally advanced only next_rollout_byte_offset by the exact duplicate record length while keeping next_rollout_ordinal unchanged at N+1
  • did not edit, delete, or renumber the canonical JSONL

Result:

  • projection caught up from 1 turn / 83 items to 2 turns / 1,064+ items and reached exact rollout EOF
  • later live appends have remained synchronized
  • app read now returns the current resumed turn instead of the stale pre-resume turn
  • SQLite integrity and foreign-key checks pass
  • no further expected ordinal N+1, got N errors occurred after the repair

This independently confirms the same narrow recovery shape reported in #41079 and reinforces the need for automatic duplicate-boundary reconciliation plus a supported doctor/repair command. No thread IDs, absolute project paths, prompts, conversation content, credentials, or raw logs are included.