Skip to content

Codex Desktop: chat history stops rendering after crash mid-turn (projection cursor never follows rollout file rotation) #42387

Open

Description

@isaac0804

What version of the Codex App are you using (from "About Codex" dialog)?

0.150.0-alpha.8 (confirmed from a local session's session_meta record; please verify against the "About Codex" dialog and correct if the app has since updated)

What subscription do you have?

Team

What platform is your computer?

Windows 11, VS Code extension source ("source":"vscode", "originator":"Codex Desktop")

What issue are you seeing?

After a turn is interrupted (app crash / force-quit mid-turn), the Desktop app's rendered chat history for that thread permanently stops updating at the exact point of the crash — even though the underlying conversation continues normally and the model still has full context of everything said afterward. Re-opening the app (once, or repeatedly) never recovers the missing history; the thread looks like it silently lost every message after the crash, while resume-based context loading proves the data was never actually lost.

In my case the crash was triggered by the system running out of memory — my machine was running many other processes at once and doesn't have much RAM to spare, so Codex Desktop (or the underlying OS) got killed mid-turn under memory pressure rather than crashing due to anything Codex-specific. I'm noting this because it means the trigger isn't an exotic edge case — any resource-constrained machine that occasionally runs low on RAM will hit this "turn interrupted mid-write" condition, so the missing-history bug is probably more common than the lack of existing reports suggests.

Root cause, traced in the open-source repo (openai/codex, main branch):

  1. codex-rs/thread-store/src/local/live_writer.rs — the rollout log path for a thread's live writer is resolved once, at resume_thread, and held statically (RolloutRecorder::new(&config, RolloutRecorderParams::resume(rollout_path))). There's no live re-pointing of an active writer to a different file mid-session.
  2. codex-rs/thread-store/src/local/thread_history_materialization.rs — read_projection_steps() opens the rollout file at whatever path it's handed once, and only ever reads forward from a stored byte offset within that specific file. It has no file-discovery or rotation-detection logic.
  3. codex-rs/thread-store/src/local/thread_history.rs — apply_projection() enforces strict offset continuity against the stored cursor:
    if expected_offset != start_offset {
        return Err(ThreadStoreError::Internal {
            message: format!("thread history projection for {thread_id} is behind durable rollout"),
        });
    }

When a turn is interrupted mid-crash and the app resumes into a new physical rollout file for the same thread_id (I observed this as a rename pattern like rollout-<ts1>-<thread_id>.jsonl → rollout-<ts2>-<thread_id>_<new_uuid>.jsonl), the new file's byte offsets restart near 0. The thread_history_projection_state row for that thread still holds next_rollout_byte_offset from the old file's EOF. Every subsequent materialization attempt then fails the expected_offset != start_offset check and errors out — permanently, since nothing resets or migrates that row when a rotation happens. The SQLite-backed thread_items/thread_turns tables that the Desktop UI reads from are frozen at the crash boundary forever, while the raw rollout log (and thus the model's resumed context) keeps growing correctly.

I confirmed this directly against a local thread_history_1.sqlite (on a copy, not the live file): thread_history_projection_state.next_rollout_byte_offset for the affected thread exactly equaled the byte size of the pre-crash rollout file, and thread_items/thread_turns had zero rows past that point despite the actual conversation continuing for many more turns across roughly a day of work in the second file.

What steps can reproduce the bug?

  1. Start a Codex Desktop session and have a multi-turn conversation.
  2. Force the app to crash or be killed mid-turn (simulating a real crash — e.g. an out-of-memory kill on a resource-constrained machine with many other processes running, which is how I hit this), such that the active turn ends up recorded as turn_aborted / interrupted in the rollout log.
  3. Reopen the app and resume the same thread. Confirm you can keep chatting and the model still has full context (e.g. ask it to repeat your last message before the crash).
  4. Check the Desktop app's rendered history pane for that thread. Expected: full history including everything after the crash. Actual: history rendering stops exactly at the crash point and never updates again, even across further app restarts.
  5. (Diagnostic, optional) On a copy of ~/.codex/thread_history_1.sqlite, query:
    SELECT next_rollout_byte_offset, next_rollout_ordinal
    FROM thread_history_projection_state WHERE thread_id = '<affected thread id>';
    and compare against the byte size of the pre-crash rollout file under ~/.codex/sessions/.../rollout-<ts1>-<thread_id>.jsonl — they should match exactly, confirming the stuck cursor.

What is the expected behavior?

When a thread's rollout log rotates to a new physical file after an interrupted-turn resume, the history materializer should detect the rotation (or the resume flow should reset/re-seed thread_history_projection_state for that thread_id against the new file) so the SQLite projection — and therefore the rendered UI history — keeps advancing instead of permanently stalling at the crash point.

Additional information

  • This does not cause data loss — the raw rollout .jsonl log and the model's resumable context are intact. It's specifically the SQLite materialization layer used to render the Desktop history pane that gets stuck.
  • I did not find an existing issue for this via GitHub code/issue search (thread_history_projection_state, is behind durable rollout, "history disappear after crash", etc. all returned zero results as of 2026-09-03).
  • No conversation content or thread IDs from my actual usage are included in this report — happy to provide a redacted thread_id or minimal repro session privately if that's useful for debugging.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    windows-osIssues related to Codex on Windows systems
    sessionIssues involving session (thread) management, resuming, forking, naming, archiving
    on Sep 2, 2026
  2. 84dnnvbdvp-debug commented on Sep 3, 2026

    @84dnnvbdvp-debug

    One caution on narrowing this to rollout-file rotation specifically: the older #41333 thread has the same frozen paginated-history / ordinal-mismatch family with intact canonical rollout data, including a measured specimen where the SQLite projection held next_ordinal=25202 while the complete rollout record at its stored byte offset was ordinal 25201. That report also notes some ultimately omitted turns completed before the time-correlated crash, so rotation/crash was not established as the necessary trigger.

    That makes file rotation a useful discriminator, but probably not the safest recovery invariant by itself. A regression/repair should also cover generic derived-projection cursor divergence from an intact canonical rollout: on a proven cursor mismatch, invalidate/reseed or rebuild only the per-thread derived projection from canonical rollout evidence rather than rewriting the rollout. If rotation is involved, binding the cursor to rollout file identity/epoch (or reseeding on path change) may be part of the fix, but same-file/off-by-one divergence should not remain permanently unrecoverable.

    This does not establish that #42387 and #41333 have one internal cause; it only argues that the recovery path should not depend on rotation being the sole cause.

  3. ORI-Fourreaumaxime commented on Sep 3, 2026

    @ORI-Fourreaumaxime

    I can confirm the same projection/rollout-handoff failure signature on Windows with a newer Codex Desktop/runtime.

    Environment:

    • OpenAI.Codex 26.831.2377.0
    • ChatGPT.exe 152.0.7977.64
    • bundled codex-cli 0.152.1
    • Windows x64

    Read-only forensic analysis of one affected long-running thread:

    RAW history since 2026-09-01:

    • 20 turns
    • 19 completed
    • 1 aborted

    Persisted thread_history_1.sqlite history since 2026-09-01:

    • 0 turns

    The Desktop historical view stops at the older rollout boundary. Interestingly, the latest live turn is still visible separately, while the 19 intermediate turns are absent from the persisted/history projection.

    Key cursor evidence:

    OLD_ROLLOUT_SIZE                    = 129482746 bytes
    PROJECTION_NEXT_ROLLOUT_BYTE_OFFSET = 129482746 bytes
    NEW_ROLLOUT_SIZE                    = 59480253 bytes
    PROJECTION_NEXT_ORDINAL             = 8011
    NEW_ROLLOUT_LAST_ORDINAL            = 17591
    

    The stored projection byte offset is therefore exactly equal to EOF of the previous physical rollout.

    state_5.sqlite resolves the thread to the newer rollout, but the historical projection cursor still carries the previous rollout's terminal byte offset.

    The newer rollout contains all subsequent turns and remains readable. No canonical conversation data appears to be lost.

    This strongly matches the rollout-file handoff/rotation failure described here: rollout identity/path advances, but the persisted history projection cursor does not appear to be rebound/reseeded for the new physical file.

    One additional observation that may help isolate the Desktop architecture: the newest live turn can still be rendered while the intermediate persisted history is absent. This suggests the realtime/live rendering path can advance independently of thread_history_1.sqlite.

    I cannot confirm that the trigger in my case is identical to the crash/OOM trigger reported here; I am only confirming the same resulting storage and projection state.

    No SQLite, rollout, index, project state or configuration was modified during this diagnosis.

  4. dnhrdt commented on Sep 7, 2026

    @dnhrdt

    Additional read-only corpus evidence from Windows (2026-09-07): this is a recurring class, not a single damaged thread.

    I scanned 2,785 rollout JSONLs. After excluding two explicit forks (forked_from_id), I found 24 rotated continuation segments across 19 logical thread IDs, dated 2026-08-20 through 2026-09-07. The continuation metadata spans CLI versions 0.148.0-alpha.15 through 0.153.4.

    For all 24 rotations:

    • history_base.thread_id resolves to the exact physical parent rollout node (the final UUID in the parent's rollout filename).
    • end_byte_offset ends at a JSONL newline, the last parent-prefix ordinal is end_ordinal_exclusive - 1, and the child session_meta ordinal is end_ordinal_exclusive.
    • The logical session ID and context_window remain unchanged.
    • The parent contains a suffix after the referenced prefix with a user turn ending in turn_aborted.
    • The child is created 5.676 to 195.333 seconds after turn_aborted (median 15.112 seconds).

    Fourteen of the 24 rotations stayed on exactly the same CLI version, so an application update is not required. This points to the generic interrupted-turn resume/rollout-rotation path rather than an OOM crash or update as the necessary trigger.

    The local state also separates into two independent pointers:

    • state_5.sqlite points to the base rollout for 17 of the 19 logical threads and to a continuation for 2.
    • thread_history_projection_state matches the current size and next ordinal of the base rollout for all 19 threads, and never the continuation leaf.
    • In both cases where state_5.sqlite points to the continuation, the history projection still points to the base rollout.

    That means updating only the thread's rollout pointer would not repair the rendered history; the projection cursor can remain independently attached to the old physical file.

    Two parent/base rollouts were written to again after their continuation had already been created (856 and 906 later records respectively). Therefore selecting a rollout by latest timestamp, largest size, or most recent modification would be unsafe. The implementation needs to honor the exact history_base lineage and preserve real branch alternatives.

    This corpus does not support the duplicate-ordinal mechanism from #41566 at these lineage boundaries: every validated parent-prefix/child boundary is consecutive. Ordinal overlap occurs across real branches after the fork point, not as a duplicate within the selected linear boundary.

    A supported reindex or branch-reconciliation path would also be valuable for threads that are already affected.

  5. MeGustas-5427 commented on Sep 7, 2026

    @MeGustas-5427

    Additional Windows report: frozen history after force-quit, plus side-chat failure

    When Codex Desktop becomes stuck while a task is still running, I have to force-close the application. After reopening Codex and opening the same task, its conversation view stays at the point where it originally got stuck. Recent conversation history does not appear.

    Once a task is affected, reopening that task repeatedly continues to show the same stale view. The side-chat panel also cannot be opened.

    Environment

    • Product: Codex Desktop for Windows.
    • Currently installed Windows app package: OpenAI.Codex 26.901.6511.0, verified from local Windows package metadata on September 7, 2026. This is the installed package version, not a version copied from the About dialog; the version at the original interruption was not separately recorded.
    • Platform: Microsoft Windows NT 10.0.26100.0 x64; Windows 24H2, OS build 26100.9168.
    • UI language: Simplified Chinese.

    Observed sequence

    I do not yet have a deterministic minimal reproduction for the initial application hang. The sequence I experienced is:

    1. Open an existing task in Codex Desktop and let it execute.
    2. Codex becomes stuck while that task is still running.
    3. Force-close the application to recover from the hang.
    4. Restart Codex and reopen the same task.
    5. Observe that the conversation still displays the old state from the interruption, and recent history is missing from the view.
    6. Attempt to open the side chat. It fails with the error below.
    7. Reopen the affected task again. The history still stops at the same old point.

    Actual behavior and visible error

    • Recent conversation history is inaccessible through the task UI.
    • Reopening the affected task repeatedly does not restore the current conversation view.
    • The side-chat panel fails to open with this exact Chinese toast:
    未能打开侧边聊天
    

    English translation: Failed to open side chat.

    The supplied screenshot shows this toast above the affected task. The text is transcribed here so it is searchable.

    Expected behavior

    After an interrupted execution and app restart, reopening a task should restore all persisted conversation history and reconcile the interrupted task state. Its side chat should remain accessible.

    If recovery cannot complete, the app should show an actionable recovery error instead of repeatedly presenting the stale conversation view. A supported way to recover the history of already affected tasks would also help.

    Impact and scope

    This prevents me from reviewing recent work and continuing reliably in an existing task after a hang.

    These are observations from the UI. I have not checked whether the missing messages remain in the underlying session files, so I cannot establish actual data loss or a storage-level root cause.

    I am adding this here because the force-quit followed by permanently stale history matches the visible symptoms in this issue. My additional observation is that the same affected task also fails to open its side chat. #42509 may be relevant to that symptom.

    I have not confirmed whether the history and side-chat failures share an internal cause, or whether my case has the storage-level mechanism discussed above.

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

    appIssues related to the Codex desktop appbugSomething isn't workingsessionIssues involving session (thread) management, resuming, forking, naming, archivingwindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions