[Desktop] Completed thread shows stale pre-crash state after restart while rollout & state DB stay intact — thread_history projection is never rebuilt
Upstream bug report — openai/codex
Title
[Desktop] Completed thread shows stale pre-crash state after restart while rollout & state DB stay intact — thread_history projection is never rebuilt
Body
Environment
- OS: Windows 11 Pro x64; every shutdown/restart in the timeline was clean (no power loss, no bugcheck)
- App: Codex Desktop for Windows, version 26.818.41509
- Observed: 2026-08-20 (all timestamps UTC)
Summary
During an active Desktop task, the Codex app-server process died abruptly
(the whole process tree was terminated externally by a local automation tool).
After relaunching the app, a completed thread appeared to have "rolled
back": its visible history and the Continue position jumped back to a much
earlier point in the conversation.
Inspecting the three storage layers under %USERPROFILE%\.codex showed the
data was not lost:
| Layer | Role | State after incident |
|---|---|---|
| sessions/<date>/rollout-*.jsonl | raw turn transcript | ✅ complete, ends with the final task_complete |
| state_5.sqlite | authoritative thread state | ✅ current; terminal state recorded |
| thread_history_1.sqlite | Desktop's UI projection of threads | ❌ frozen at a pre-crash position |
Quantified: rows materialized in the projection covered roughly 16% of the
event range the authoritative layer held for this thread, and that number
never changed across multiple app restarts over the following two days.
What appears to be wrong
- The
thread_history_*projection appears to be written incrementally as
events arrive, with no reconciliation pass on startup: nothing compares
the projection against the authoritative state store and catches it up.
- There is no rebuild/fallback path when the projection is detected as stale
or torn (e.g., writer died mid-write).
- History view and Continue/resume appear to serve from the projection,
so a stale projection hides real work the model actually did — worse than
losing data, it misrepresents it.
- Projection writes are not crash-atomic with the authoritative store, and
the crash window (app-server death between the two writes) leaves them
permanently divergent.
How it manifested
- Thread list showed the thread with its old preview/position.
- Opening it displayed history only up to the freeze point.
- Continue resumed from the stale position even though later turns existed in
both the rollout and the state DB.
- Reinstalling the app did not repair the projection (fresh binaries, same
frozen thread_history_1.sqlite), confirming it lives outside the package.
Minimal reproduction sketch
- Start a Desktop task; let several turns complete.
- Hard-kill the app-server / process tree mid-task (simulating a crash).
- Relaunch Desktop.
- Observe: thread history and Continue position revert to some earlier point;
rollout JSONL and state_5.sqlite still contain everything after it.
(The triggering crash in our case came from a third-party local tool killing
the process tree; the defect itself — no projection reconciliation on restart —
is independent of what caused the death.)
Expected behavior
On startup (or when opening a thread), Desktop reconciles the UI projection
against the authoritative state store — or simply treats the projection as a
cache that can always be rebuilt from state_5.sqlite / rollouts — so a
completed thread can never present stale history after a crash.
Actual behavior
The frozen projection persists indefinitely across restarts and even across an
application reinstall (fresh binaries, same frozen thread_history_1.sqlite,
confirming the projection lives outside the package). The thread's visible
history and Continue position stay pinned to the pre-crash point while both
durable stores contain the full completed session. Recovering the correct view
currently requires out-of-band surgery on private SQLite files, which end
users should never have to perform — and which we deliberately did not attempt.
Impact
- Completed work becomes invisible after any crash landing inside the
projection-writer window; the UI actively misrepresents the durable record.
- Continue can resume from a stale point, inviting duplicated work.
- User trust in the history view erodes because it can contradict
state_5.sqlite / rollouts, which are supposed to be the source of truth.
Notes for maintainers
- We can supply, privately, the exact row-range comparison between
thread_history_1.sqlite and state_5.sqlite (materialized vs authoritative
event coverage), the crash-window log correlation, and the full restart
timeline. This public report intentionally omits thread identifiers,
absolute paths, and any conversation content.
- A preserved evidence snapshot of the divergent databases is available if a
fix build needs a real-world regression target.
2 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Marking as duplicate of #38792, which describes the same \ hread_history_1.sqlite\ projection cursor desync and permanent non-repair at greater depth (our case is the Windows Desktop manifestation). Consolidating our quantitative evidence (16% coverage vs authoritative store, reinstall-persistence, Desktop Continue/list symptoms) into that thread.