Codex local history index becomes stale and recent sessions cannot be resumed

Resolved 💬 2 comments Opened Apr 27, 2026 by nobienmei Closed Apr 28, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Bug Report: Codex local history index becomes stale and recent sessions cannot be resumed

Summary

Codex VSCode/local history sometimes fails to show or resume recent sessions even though the actual session data still exists on disk.

This appears to be an index synchronization bug in Codex's local history management.

Environment

  • Product: Codex VSCode integration / local Codex
  • CLI version observed in session metadata: 0.125.0-alpha.3
  • Platform: Linux
  • Source in local thread DB: vscode
  • Model configured: gpt-5.5

Impact

Recent Codex conversations can disappear from the visible history/resume list. This causes repeated context loss and additional token usage because the agent cannot reliably load the immediately prior work.

This is especially costly for long-running coding and research sessions where continuity is expected.

Evidence

Recent sessions existed in the local Codex data store:

  • ~/.codex/state_5.sqlite
  • ~/.codex/sessions/YYYY/MM/DD/rollout-...jsonl

However:

  • ~/.codex/session_index.jsonl was stale and only listed sessions up to the previous day.
  • The current-day session was missing from the visible/resume history index.
  • state_5.sqlite integrity check returned ok.
  • logs_2.sqlite integrity check returned ok.

Additionally, an internal approval-review/guardian thread was inserted into the normal threads table:

  • source: {"subagent":{"other":"guardian"}}
  • model: codex-auto-review
  • title: began with The following is the Codex agent history whose request action you are assessing...

This internal review thread appeared alongside user-facing threads and polluted the history table.

Observed State Before Repair

The local threads table contained the recent user-facing thread:

  • archived: 0
  • source: vscode
  • rollout_path: pointed to an existing ~/.codex/sessions/.../rollout-...jsonl

But session_index.jsonl did not include it until manually rebuilt.

Manual Repair Performed

The following local repair was performed:

  1. Backed up:
  • config.toml
  • session_index.jsonl
  • state_5.sqlite
  • logs_2.sqlite
  1. Rebuilt session_index.jsonl from state_5.sqlite.
  2. Marked internal guardian / codex-auto-review threads as archived so they do not appear as normal user history.
  3. Ran SQLite WAL checkpoints.

After repair, the missing recent session appeared in session_index.jsonl.

Expected Behavior

  • Recent user-facing sessions should always appear in the history/resume list if the session exists in state_5.sqlite and the rollout JSONL exists.
  • session_index.jsonl should stay synchronized with state_5.sqlite.
  • Internal approval-review / guardian / codex-auto-review threads should not be inserted into, or at least not surfaced through, normal user-facing history.
  • If session_index.jsonl is stale or corrupt, Codex should rebuild it automatically from state_5.sqlite and/or sessions/**/*.jsonl.

Actual Behavior

  • Recent sessions existed on disk but were missing from the history index.
  • The visible history could not reliably load the latest work.
  • Internal review-agent threads appeared in the same local thread store as normal user sessions.

Requested Fix

Please fix Codex local history handling so that:

  1. session_index.jsonl is transactionally updated or automatically regenerated when stale.
  2. Recent sessions are discovered from state_5.sqlite / rollout JSONL even if the index is stale.
  3. Internal guardian / approval-review threads are excluded from user-facing history.
  4. WAL/checkpoint behavior cannot leave the UI with a stale history list after a normal session.

Severity

High for paid users relying on Codex continuity. The issue causes context loss, duplicate work, and avoidable token spend.

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 4 months ago

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

  • #18364

Powered by Codex Action

leadingproblemsolver · 20 days ago

I would make one architectural rule explicit here: session_index.jsonl should be a rebuildable projection, never an authority for session existence.

If the durable sources are something like:

state_5.sqlite thread row
+ rollout JSONL existence / metadata

then history listing can reconcile the index against those sources rather than failing closed on an incomplete index.

A useful contract could be:

type IndexedSession = {
  threadId: string;
  rolloutPath: string;
  sourceMtime: number;
  indexedAt: number;
};

with reconciliation roughly:

1. read index
2. enumerate authoritative session/thread inventory
3. add durable sessions missing from index
4. remove/index-mark entries whose durable source is gone
5. exclude internal thread kinds from user projection
6. atomically publish rebuilt index

The important failure rule is:

index says absent + durable source says present
=> session exists; index is stale

not the other way around.

I would also persist enough provenance on an index entry to detect silent staleness without waiting for a user-visible failure: source path, source mtime/hash or DB revision, and last indexed time. Then startup/listing can cheaply answer whether a row is still backed by the version it was derived from.

For crash consistency, rebuilding into a temp file and atomic rename is safer than mutating session_index.jsonl in place. If WAL/checkpoint state means SQLite is newer than the index, reconciliation simply regenerates the projection.

A regression fixture could deliberately create the reported split:

DB:      threads A,B,C
rollout: A,B,C exist
index:   A,B only
=> history shows A,B,C
=> rebuild produces A,B,C

plus the inverse stale-entry case:

index:   A,B,C
DB/source: A,B
=> C is not surfaced as a valid resumable user session

I maintain a small local-first project that uses the same evidence/projection boundary—durable source-backed records in SQLite with source path/line/content-hash provenance, while query/context views are derived: https://github.com/leadingproblemsolver/living-context-engine

The relevant principle here is simply that a convenience index can be thrown away and rebuilt from authoritative evidence; losing or staling the projection should never make the underlying work disappear.