Repository navigation
macOS: newest-first thread lookup returns two-day-old in-progress turn despite newer persisted messages #43129
Description
Recent conversation lookup returns two-day-old turns while newer messages remain in the local transcript
Environment
- Observed September 5, 2026, around 8:33 PM America/New_York (September 6, 00:33 UTC).
- macOS 26.5.2, arm64.
- Long-running local Codex conversation spanning multiple days.
- Installed desktop/build version was not established. The documented compatibility bundle path was absent; no version is inferred.
User-visible symptom
The user reports that substantial recent conversation history disappeared from the displayed chat and that the conversation appears to have reverted approximately two days. The UI symptom is user-reported; the retrieval mismatch below was independently observed.
Independently verified evidence
- Read the affected thread using the available
read_threadapp tool withincludeOutputs: false, without a pagination cursor. - A request for the newest eight turns returned old September 3 work. Repeating with
turnLimit: 2returned the same old newest turn. - The second response declared
page.order: newest_first,page.limit: 2, andhasMore: true. - Its newest returned turn had
startedAt: 1788468606(September 3, 2026, 20:50:06 UTC),status: inProgress, andcompletedAt: null. - The thread metadata was current:
updatedAt: 1788654804(September 6, 2026, 00:33:24 UTC), despite returning a September 3 turn as newest. - A read-only inspection of the matching local session JSONL file found newer user/assistant message records dated September 6 at 00:32:32, 00:33:06 and 00:33:08 UTC, including the user's current report. The file was approximately 147 MiB at inspection.
- Independently, the working repository retained the latest same-evening committed work. This is not evidence of a repository rollback.
Expected behavior
The current conversation view and a cursor-free newest-first thread lookup should expose the latest persisted messages, with consistent turn status and ordering. Pagination should not silently make a multi-day-old turn appear to be the latest available work.
Actual behavior and impact
The newest-first lookup is inconsistent with current thread metadata and recent persisted messages. The visible chat reportedly loses approximately two days of history. This undermines confidence in completed work and could cause an operator or agent to repeat already completed actions based on stale history.
Reproduction limits / hypotheses
- Confirmed twice against one affected existing thread; no minimal synthetic reproduction yet.
- Recent sampled messages are present on disk. Completeness of every intervening message has not been audited, so this report does not claim universal absence of data loss.
- A stale index, incorrect pagination boundary, or turn reconstruction problem is a working hypothesis, not a proven cause.
- No cache deletion, transcript rewrite, database mutation, restart, or recovery action was performed during diagnosis.
Requested investigation
Please investigate how the desktop conversation view and read_thread newest-first pagination reconstruct long-running turns from persisted session records, particularly when an old turn remains marked in progress despite newer records and current thread metadata.
Related reports
- macOS desktop history freezes at older turns: projection expected ordinal 3185, got 3184; migration says already_paginated #43124 describes a similar macOS stale-history symptom with a verified projection ordinal mismatch. This report has not inspected projection ordinals and does not claim that cause.
- [Desktop][Windows] Paginated thread history stalls on duplicate ordinal while rollout remains complete #41079 describes Windows paginated history stalling while the rollout remains complete.
- [Windows Desktop] Switching ChatGPT accounts restores a stale turn for the same local Codex task #42971 describes recent metadata with an older in-progress turn after account switching on Windows. No account-switch trigger has been established in this case.
Privacy
No raw transcript, screenshots, log attachments, conversation text, thread/session identifiers, project/repository names, hostnames, local account paths, credentials, tokens, URLs from captured activity, or production data are included. This report contains only a manually selected symptom description, software environment and diagnostic timing/status metadata.
Activity
github-actions commentedon Sep 6, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
argszero commentedon Sep 6, 2026
Hi — this looks like the same underlying thread-history projection family as #43124 (which I just root-caused on
main). In both, the durable SQLite projection (thread_history_projection_state) has stalled behind the intact append-only transcript, so reads return an old turn even though newer messages exist in the rollout.The code mechanism is at
codex-rs/thread-store/src/local/thread_history.rs:144-149:apply_projectionraisesexpected ordinal {next_ordinal}, got {ordinal}when the storednext_rollout_ordinalno longer matches the next line's ordinal at the stored byte checkpoint. Once the projection state is transactionally half-advanced,migrate-rolloutsreturnsalready_paginated(rollout_migration.rs:513-517) without checking projection completeness — so there is no repair path and the stall is permanent.Linking these reports helps the maintainers see the full repro range (different clients, same projection-stall signature). I posted a detailed diagnostic + two suggested fix directions on #43124; if you can confirm the
next_rollout_byte_offset/next_rollout_ordinalvalues and the rollout byte at that offset, it would pin the divergence cause in both.killersteps commentedon Sep 6, 2026
Adding an independent reproduction: in a long-running local conversation,
read_threadrepeatedly returns an old in-progress turn as the newest turn, even though the raw session JSONL contains multiple subsequently completed turns and the current user message.Environment
26.901.51231, build8109, read from the package'sInfo.plist.session_meta.cli_version:0.153.4.Darwin 25.6.0 arm64 arm.2026-09-06T02:54:11Z.Independently verified observations
Repeated calls to the desktop-provided
mcp__codex_app__read_threadtool, without a pagination cursor, returned the same old turn as newest. This occurred with bothturnLimit: 2andturnLimit: 3.The most recent request was as follows, with the thread ID redacted:
{ "threadId": "<affected-thread>", "turnLimit": 2, "includeOutputs": false, "maxOutputCharsPerItem": 200 }The relevant response fields are summarized below.
newestReturnedTurnis a descriptive wrapper for the first returned turn, anditemCountis derived from its item array. The thread metadata was current at query time, but the turn presented as newest had started approximately six hours earlier:{ "thread": { "updatedAt": 1788663251 }, "page": { "order": "newest_first", "limit": 2, "hasMore": true }, "newestReturnedTurn": { "status": "inProgress", "startedAt": 1788641639, "completedAt": null, "itemCount": 4 } }That
startedAtcorresponds to2026-09-05T20:53:59Z. A read-only scan of the raw session JSONL for the same thread confirmed that these later records exist:task_complete, with a non-emptylast_agent_messagetask_complete, with a non-emptylast_agent_messagetask_complete, with a non-emptylast_agent_messageresponse_itemmessageThese timestamps come from actual event or message records, not file modification times or historical text quoted inside tool output. The raw records and tool requests refer to the same thread; its identifiers and conversation content are withheld for privacy.
Impact and expected behavior
The conversation appears to have reverted to an earlier stage of work. An agent relying on this history can incorrectly conclude that completed work is unfinished, overlook subsequent requirements, or repeat completed operations.
A cursor-free newest-first history request should expose the latest persisted turns. If history reconstruction is incomplete, the tool should return an explicit error or incomplete-state marker rather than silently return stale turns as a successful result.
Diagnostic limits
read_threadtool was directly tested. The underlyingthread/readandthread/turns/listmethods were not called directly.Stahl-G commentedon Sep 7, 2026
I am also encountering this issue in a long-running Codex desktop conversation.
The app's
read_threadtool, called without a cursor and requesting the newest turns, returns an older in-progress turn rather than the latest messages. Repeating the lookup with a smaller turn limit returns the same stale turn. Newer messages are still present in the local transcript, so the lookup does not reflect the latest persisted conversation.General reproduction steps:
read_thread, without a pagination cursor.Expected: the newest-first lookup returns the most recent persisted messages.
Actual: it can return an old turn as the newest, giving the impression that recent history has disappeared.
I have not established a minimal synthetic reproduction or root cause. No private transcript, identifiers, timestamps, file paths, storage metadata, or task data are included in this report.
kernelpanic888 commentedon Sep 14, 2026
Issue: #43129.
Minimal projection-lag fault injection, not a reproduction of the original writer failure
I tested official app-server 0.154.0 on macOS 26.5.1 arm64 through stdio, with synthetic files only. This narrows the public read/recovery behavior; it does not invoke the closed desktop
read_threadwrapper or establish how the original inconsistent state arose.Procedure:
migrate-rollouts --apply --json --thread <synthetic-id>in the isolated test home. Both migrations reportmigrated; the complete control returns its newer completed turn.thread/read(includeTurns:true)andthread/turns/list(limit:2,sortDirection:desc,itemsView:full).already_paginated, zero bytes processed; still older turn onlyThe stored older turn is
interruptedhere, whereas the reported desktop case wasinProgress. This is an intentional fault-injection example of stale-success behavior, not an exact reproduction of all UI symptoms. A separate normal legacy append/partial-line control does expose the latest turn after append and after restart.Source connection and remaining question
The release's paginated list_turns validates metadata and reads projected rows, without materializing the unread durable tail at that entry point. The existing already-paginated migration branch is not a general projection-repair operation. This explains the observed fixture; it does not identify the original failure that left a projection behind.
Current materialization includes handling for gaps/duplicate ordinals, so the earlier ordinal-mismatch theory must be checked against the affected binary, not assumed unchanged. No production data or projection table was edited in this investigation.
Suggested regression/design question: when a paginated read knows its durable tail is ahead of its projection, should it perform a bounded catch-up under the appropriate locks, return an explicit incomplete/recovering state, or expose a safe repair operation? Returning ordinary success conceals this distinction. Avoid an unconditional full-log scan: the related large-history reports show why that would be risky.
Reproducer:
app-server-harness.mjsandprojection-lag-repro.mjs. Runnode projection-lag-repro.mjsbeside the official macOS-arm64 binary. The migration/resume operations are restricted to generated fixtures. This is not a recommendation to run repair commands on personal conversations, nor proof that resume repairs the original reported failure.Alexey Salkutsan selected the investigation. Lina, an AI assistant, authored and ran the harness and prepared this analysis. The previous reporters' observations and hypotheses remain theirs.
Reproducer package
Download the synthetic reproductions and reader audit. The same small packet supports this issue and the related investigations in #43017, #43129 and #38611. It contains no personal histories, databases, raw RPC logs or bundled executable. Original third-party reader code is fetched by the setup with a pinned hash and attribution.
Verified after extraction into a clean directory on macOS arm64 with Node 24.19.0. Read
README_EN.md, then runnode bootstrap.mjsandnode verify.mjs --portable; setup downloads pinned public dependencies and the official binary. The clean verification used the already downloaded, hash-verified official binary archive with--archive; it fetched the reader and npm dependencies afresh. Linux, Windows and the uncached binary-download branch were not tested. The ZIP contains the earlier preparation drafts, whose NOT SENT headings describe the pre-publication snapshot.ZIP SHA-256:
2d60212455f72dddfd52410b3c8cc55e826ba2900e0813e26c070d65566972fb.