Skip to content

Thread store resolves stale session_index entries to missing rollout files (state db returned stale rollout path) #31074

Open

Description

@ViperJuice

Summary

The local session index (~/.codex/session_index.jsonl) retains entries whose rollout
JSONL no longer exists in either the live sessions tree or archived_sessions/. When the
app/thread store resolves such a thread id, it returns a path to a missing file and errors
instead of falling back to the archived copy or self-healing the index. On a
long-lived, high-session-volume host this accumulates: 113 of 283 indexed entries (40%)
had no backing rollout file anywhere.

Evidence

Codex's own log reports the inconsistency from inside codex_rollout::list:

ERROR codex_rollout::list: state db returned stale rollout path for thread
019f2339-9299-7e51-b192-9751a0a03586:
/home/USER/.codex/sessions/2026/07/02/rollout-2026-07-02T14-26-43-019f2339-...jsonl

That file is not in live sessions/, not in archived_sessions/ (507 archived JSONLs
present), and not found by a recursive basename search.

Audit of the index on the affected host:

index entries: 283 | present (live or archived): 170 | STALE (no backing file): 113

A second, related failure mode also seen: a rollout file that exists but is misfiled —
stored under sessions/2026/05/09/ while its contents begin with 2026-07-03 data and do
not start with a session_meta record. So the index/path bookkeeping and the on-disk
partitioning can disagree in both directions (missing file; misplaced/malformed file).

How it likely arises

The index write and the rollout file lifecycle (create / archive-move / delete) are not
crash-consistent. If the process is interrupted between updating the index and finalizing
the file — e.g. an ENOSPC disk-full (our trigger: root fs hit 100%), a SIGKILL, or an
archival move that half-completes — the index keeps an entry the filesystem can't satisfy.
Nothing later reconciles it.

Impact

  • App/thread resolution errors when opening or listing affected threads.
  • Users cannot cleanly resume or reference historical sessions that are actually still
    recoverable from durable artifacts.
  • Grows unbounded on active hosts; 40% stale observed.

Expected behavior (any of)

  1. On resolve-miss, fall back to archived_sessions/ (and a basename search) before erroring.
  2. Self-heal the index: drop or flag entries whose rollout file is absent from both live
    and archived locations, rather than repeatedly resolving to a dead path.
  3. Make the index update + rollout finalize crash-consistent (write file first / fsync +
    atomic rename, update index only after), so an interrupted write can't orphan an entry.
  4. Provide a codex subcommand to verify/repair the session index.

Workaround in use

Local reconciliation script: back up session_index.jsonl, drop entries with no backing
rollout file in live or archived sessions (never deletes rollout files). Mitigates the
resolve error until a first-class fix lands.

Related issues (adjacent but distinct)

Several open issues touch "stale rollout path," but each is tied to a specific Desktop UI
flow rather than the crash/interruption-consistency root cause and the systemic 40%
accumulation reported here (observed via codex-cli / app-server, not a UI action):

This report is about the index and rollout-file lifecycle becoming inconsistent when a
write is interrupted (ENOSPC / SIGKILL / half-completed archival move), independent of
any UI flow, and the index then never self-reconciling. Please link/dedupe as appropriate.

Environment

  • codex-cli 0.142.5, Linux (Ubuntu 22.04), standalone install
  • ~/.codex on local disk; hundreds of live + 507 archived sessions
  • Trigger correlated with a root-filesystem ENOSPC event (0 bytes free) during an archival burst

Activity

  1. added
    bugSomething isn't working
    CLIIssues related to the Codex CLI
    sessionIssues involving session (thread) management, resuming, forking, naming, archiving
    app-serverIssues involving app server protocol or interfaces
    on Jul 4, 2026
  2. ideajoker commented on Jul 8, 2026

    @ideajoker

    版本 26.623.141536 • 发布于 2026年7月7日

    no rollout found for thread id

  3. Odezra commented on Jul 9, 2026

    @Odezra

    Additional macOS evidence showing a one-for-one state transition after a legacy filesystem-level archive move:

    • Standalone CLI: 0.143.0
    • Desktop-bundled CLI: 0.144.0-alpha.4
    • SQLite integrity checks: all OK
    • A legacy local archive helper moved 118 rollout JSONL files from the active sessions tree to the archived sessions tree. Every file was preserved; there was no hard deletion. That helper is now paused.

    Before and after codex doctor --json counts changed exactly with the moved set:

    archived rollout files missing state DB rows: 1,028 -> 1,146 (+118)
    stale state DB rows pointing at former active paths: 668 -> 786 (+118)
    duplicate thread IDs: 7
    archive path mismatches: 0
    

    This supports the report's hypothesis that filesystem-level archival can leave both sides of the inventory inconsistent: the preserved archived rollout is unindexed, while the DB row remains at the old active path.

    No manual SQLite repair or speculative filesystem rollback has been attempted. The archive workflow is paused pending a supported recovery path.

    Could Codex expose a previewable reconcile/reindex command that:

    1. Recognizes preserved rollouts relocated between the active and archived trees.
    2. Updates thread metadata without deleting history.
    3. Reports duplicate thread IDs and planned changes before applying them.
    4. Can be verified by rerunning codex doctor --json.

    A full redacted Doctor report can be provided privately if useful.

  4. xiaodeng2025 commented on Aug 18, 2026

    @xiaodeng2025

    Windows Desktop confirmation, with a newer app package.

    • Platform: Windows x64
    • App package: OpenAI.Codex_26.814.5167.0_x64
    • Onset: immediately after updating on 2026-08-18
    • At least three local Codex threads are affected.

    Observed behavior:

    • The threads remain visible in the sidebar.
    • Opening at least one shows thread not found and it cannot be continued.
    • Archiving fails, which also blocks cleanup/deletion.

    Exact archive error:

    failed to archive session: thread-store internal error:
    failed to archive thread: The system cannot find the file specified. (os error 2)
    

    One affected thread ID:
    01a013b2-9800-75d0-ae83-35e9dbc50a8e

    The task record is still readable through the local task listing, and its workspace directory still exists. I have not performed a filesystem or SQLite audit, so I cannot confirm that this is the same missing-rollout mechanism as the original report; it is a symptom-level match. I have deliberately not reset the app, cleared .codex, or deleted the workspace, to avoid making recoverable local history less recoverable.

    A safe, previewable reconcile/reindex tool or an option to remove an orphaned sidebar entry would be especially useful here.

  5. xulfereht commented on Aug 31, 2026

    @xulfereht

    Confirming this is still present on CLI 0.151.0 (current as of 2026-08-31). codex doctor surfaces it as a first-class warning now:

    ⚠ threads      state DB rows point at missing or unusable rollout files
       rollout DB stale row sample ~/.codex/sessions/2026/0…a8b5-8379553cbde6.jsonl
       rollout DB stale row sample ~/.codex/sessions/2026/0…bd78-cc681d27e1f4.jsonl
       ...
    

    on a host with 630 active rollout files (6.84 GiB) — so doctor is aware of the inconsistency but doesn't offer a --fix/reconcile action, and I couldn't find one elsewhere in the CLI (migrate-rollouts inspects/migrates legacy rollouts into paginated thread history, it doesn't reconcile stale index rows). A codex doctor --fix or a dedicated codex reindex-style command that drops/repairs stale session_index/thread-store rows for files confirmed missing (with the archived-copy fallback this issue already asks for) would close the loop doctor's own diagnostic opens.

  6. kip-claw commented on Oct 14, 2026

    @kip-claw

    Follow-up with a much more extreme ratio than previously reported, plus a downstream consequence worth flagging: this appears to directly cause a significant startup-performance regression when codex runs as an embedded harness inside another process (OpenClaw).

    Scale

    rollout DB active files:    1,033
    rollout DB rows:            19,046
    rollout DB active rows:     19,046
    rollout DB stale rows:      18,013   (94.6% of all rows)
    rollout DB missing active rows: 0
    rollout DB archive mismatches:  0
    

    94.6% stale is a much higher ratio than the 40% in the original report — on a host that's been in continuous daily use (not crashed-and-abandoned), with rollouts spanning a continuous ~2-month window (Aug 19 - Oct 14) with no gap. codex doctor correctly surfaces this as ⚠ threads state DB rows point at missing or unusable rollout files, matching the warning text quoted in earlier comments here.

    Confirms the proposed crash-inconsistency root cause

    This host has experienced, independently and repeatedly over the measurement period: multiple ENOSPC: no space left on device events (from an unrelated upstream openclaw/openclaw bug that fills a shared tmp directory — openclaw/openclaw#152689), and at least one hard SIGKILL of the parent process group during an unresponsive shutdown (openclaw/openclaw's own supervisor escalating past a graceful SIGTERM because the process was too busy to respond in time). Both match the disk-full/SIGKILL triggers this issue's root-cause theory identifies. Given how consistently this host hits exactly those trigger conditions, a 94.6% stale ratio over ~2 months of real usage seems like strong supporting evidence for "nothing later reconciles it" — the index just keeps accumulating dead entries indefinitely with no decay or self-heal.

    No officially-supported recovery path found

    I looked for a safe way to clean this up before filing:

    • codex doctor — diagnoses only, no --fix.
    • codex migrate-rollouts --apply — ran cleanly (186 migrated, 0 failed), but addresses a different concern (legacy flat-file format → paginated format) and left the stale-row count and state_5.sqlite file size completely unchanged (993MB before and after).
    • No bulk archive/delete/vacuum/gc command exists; archive/delete only operate one session at a time, and these stale rows don't correspond to any real session to target anyway.
    • I deliberately did not attempt manual SQLite surgery on state_5.sqlite — we've been burned before by in-place modification of a sibling codex database (logs_2.sqlite) desyncing its sqlx migration state and breaking app-server initialization, so improvising a fix on state_5.sqlite without guidance felt too risky.

    Downstream consequence: this is a measurable factor in slow Gateway cold starts

    For context (full details in openclaw/openclaw#162211, a startup-performance issue in the OpenClaw project that embeds codex as a harness): on this same host, the codex plugin is one of three official plugins responsible for ~90 of ~123 seconds of Gateway cold-start time, with the embedding process's own event loop fully blocked for the duration. state_5.sqlite at 993MB is one of the largest files codex touches at startup, and its size is apparently driven almost entirely by this stale-row accumulation rather than real session volume (1,033 real files vs. 19,046 indexed rows). I can't prove the stale rows are the cause of codex's slow load specifically — I don't have a way to compare against a clean index on this host — but it's a very plausible contributor, and a decay/cleanup mechanism for this index would likely help both issues at once.

    Happy to share the full codex doctor --json output (redacted) if useful.

  7. kip-claw commented on Oct 15, 2026

    @kip-claw

    Follow-up to my comment above: I tested the hypothesis directly, and it's wrong — posting the negative result so it doesn't mislead anyone chasing the same lead.

    With the Gateway stopped, I moved state_5.sqlite (993MB) and thread_history_1.sqlite (394MB) aside entirely and let codex recreate them fresh on next start (confirmed via codex doctor afterward: clean index, 0 stale rows). Measured the codex plugin's load time inside the embedding Gateway across three restarts before and after:

    • Before (bloated state, 19,046 rows / 94.6% stale): codex load time ~17-18s, every restart
    • After (fresh, empty state): codex load time ~17.3s — no measurable difference

    Total Gateway cold-start time was also unchanged (118s vs. 116-138s baseline). So the stale-row bloat in state_5.sqlite is real and worth fixing on its own merits, but it is not the cause of codex's slow plugin-load time when embedded in OpenClaw — that bottleneck is somewhere else. Restored the original files afterward (no reason to lose session history for a fix that didn't work).

    Didn't mean to send anyone down a dead end — wanted to correct the record once I had the data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIapp-serverIssues involving app server protocol or interfacesbugSomething isn't workingsessionIssues involving session (thread) management, resuming, forking, naming, archiving

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions