openai / openai/codex

I recovered a long-running Codex thread with missing history | Chat History is not beeing Displayed

Open
#43,262 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app bug session windows-os
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of the Codex App are you using (From “About Codex” dialog)?

26.904.11930

What subscription do you have?

Pro

What platform is your computer?

Windows

What issue are you seeing?

I managed to fully recover a Codex conversation on Windows where almost the entire visible history disappeared after restarting the desktop app.

The important part: the conversation data itself was not lost. The rollout JSONL was still complete and continued growing, but the thread-history projection was stuck.

My affected thread:

Started: 2026-08-29
Still actively written on: 2026-09-06
Rollout size: 608,475,078 bytes
Codex CLI stable version: 0.153.4
Windows desktop app showed only the beginning of the conversation
codex resume initially also showed only the beginning
The agent could still continue from the correct project/task state

After recovery:

Projection reached the exact end of the 608 MB rollout
27,857 thread items were materialized
42 turns were restored
The complete conversation became visible again in the Windows desktop app

  1. Verify that the original rollout is still there

I checked the most recently modified rollout files:

Get-ChildItem "$env:USERPROFILE.codex\sessions" -Recurse -Filter "*.jsonl" |
Sort-Object LastWriteTime -Descending |
Select-Object -First 20 FullName, LastWriteTime, Length

My affected rollout was:

CreationTime : 29.08.2026 00:13:15
LastWriteTime : 06.09.2026 19:45:32
Length : 608475078

So although the thread was created on August 29, the same rollout was still being updated on September 6.

I also searched the JSONL for text from recent messages that were no longer visible in the UI, and the messages were still present.

  1. Back up the rollout before doing anything
    Copy-Item $f.FullName "$env:USERPROFILE\Desktop\codex-session-backup.jsonl"

Do not modify the original rollout JSONL.

  1. Run Codex Doctor

I updated the CLI and ran:

npm install -g @openai/codex@latest
codex doctor --all --no-color

With Codex 0.153.4, all databases reported healthy:

state DB integrity ok
log DB integrity ok
goals DB integrity ok
memories DB integrity ok
thread history DB integrity ok

The rollout inventory and database inventory also matched.

So this was not normal SQLite corruption.

  1. Scan the rollout for broken ordinal sequences

I used this PowerShell script:

$prev = $null
$lineNo = 0

Get-Content -LiteralPath $f.FullName -ReadCount 1 | ForEach-Object {
$lineNo++

if ($_ -match '"ordinal"\s*:\s*(\d+)') {
    $ord = [long]$Matches[1]

    if ($null -ne $prev -and $ord -ne ($prev + 1)) {
        [pscustomobject]@{
            Line     = $lineNo
            Previous = $prev
            Current  = $ord
            Delta    = $ord - $prev
        }
    }

    $prev = $ord
}

} | Select-Object -First 30

It found two duplicate ordinals:

Line Previous Current Delta
21965 21963 21963 0
87107 87104 87104 0

The first duplicate was:

ordinal 21963 event_msg token_count
ordinal 21963 event_msg task_started
ordinal 21964 world_state

The second one was:

ordinal 87104 event_msg token_count
ordinal 87104 event_msg thread_settings_applied
ordinal 87105 event_msg thread_settings_applied
5. The thread-history projection also had an initial cursor desync

For my thread, thread_history_1.sqlite contained:

Projection state: (122207, 16)
Thread items: (3, 12)
Thread turns: (1, 1)

However, the rollout records around that byte offset were:

offset 119977 ordinal 14 response_item custom_tool_call
offset 120576 ordinal 15 response_item custom_tool_call_output
offset 121356 ordinal 16 event_msg token_count
offset 122207 ordinal 17 event_msg item_completed

So the byte cursor had already moved past ordinal 16, but next_rollout_ordinal still expected 16.

In other words:

byte cursor -> ordinal 17
expected ordinal -> 16

This caused the projection to fail immediately with:

expected ordinal 16, got 17
6. Back up thread_history_1.sqlite

Before changing anything:

$hist = "$env:USERPROFILE.codex\thread_history_1.sqlite"
$bak = "$env:USERPROFILE\Desktop\thread_history_1-before-repair-$(Get-Date -Format yyyyMMdd-HHmmss).sqlite"

python -c "import sqlite3,sys; s=sqlite3.connect(sys.argv[1]); d=sqlite3.connect(sys.argv[2]); s.backup(d); d.close(); s.close()" "$hist" "$bak"

Important: if restoring an SQLite backup later, do not blindly overwrite the main .sqlite file while old -wal or -shm files are still present.

I did this during testing and temporarily got:

wrong # of entries in index idx_thread_items_user_messages

The backup itself was healthy; the problem was mixing the restored DB with WAL state from the newer database.

  1. Fix the initial cursor desync

With Codex completely closed, I corrected only the expected ordinal.

For my thread:

Before: byte offset 122207, expected ordinal 16
After: byte offset 122207, expected ordinal 17

The byte offset stayed unchanged.

I used a guarded update so it would abort if the database did not contain the exact expected values:

$env:CODEX_THREAD_ID = ""
$env:CODEX_HISTORY_DB = "$env:USERPROFILE.codex\thread_history_1.sqlite"

@'
import os
import sqlite3

db = os.environ["CODEX_HISTORY_DB"]
tid = os.environ["CODEX_THREAD_ID"]

con = sqlite3.connect(db)

try:
before = con.execute("""
SELECT next_rollout_byte_offset, next_rollout_ordinal
FROM thread_history_projection_state
WHERE thread_id = ?
""", (tid,)).fetchone()

print("Before:", before)

# THESE VALUES WERE SPECIFIC TO MY THREAD.
if before != (122207, 16):
    raise RuntimeError(
        "Unexpected projection state; nothing changed: " + repr(before)
    )

with con:
    cur = con.execute("""
        UPDATE thread_history_projection_state
        SET next_rollout_ordinal = 17
        WHERE thread_id = ?
          AND next_rollout_byte_offset = 122207
          AND next_rollout_ordinal = 16
    """, (tid,))

    if cur.rowcount != 1:
        raise RuntimeError("Expected exactly one row to change.")

after = con.execute("""
    SELECT next_rollout_byte_offset, next_rollout_ordinal
    FROM thread_history_projection_state
    WHERE thread_id = ?
""", (tid,)).fetchone()

print("After:", after)
print("Integrity:", con.execute("PRAGMA integrity_check").fetchone())

finally:
con.close()
'@ | python -

Result:

Before: (122207, 16)
After: (122207, 17)
Integrity: ('ok',)

Do NOT copy my numeric values. They were specific to my rollout.

  1. After fixing that, Codex 0.153.4 exposed the next problem

The logs then changed from:

expected=16 got=17

to:

expected=21964 got=21963

Example:

level=WARN
expected=21964
got=21963
target=codex_thread_store::local::live_writer

This matched the first duplicate ordinal in the JSONL exactly:

21963 token_count
21963 task_started
21964 world_state

So stable Codex 0.153.4 was now getting past the initial cursor problem, scanning the rollout, reaching the duplicated ordinal, and aborting the complete history projection there.

  1. Codex 0.154.0-alpha.3 successfully rebuilt the thread

This was what finally recovered the complete history.

I first created another SQLite backup:

$hist = "$env:USERPROFILE.codex\thread_history_1.sqlite"
$bak2 = "$env:USERPROFILE\Desktop\thread_history_1-before-alpha-rebuild-$(Get-Date -Format yyyyMMdd-HHmmss).sqlite"

python -c "import sqlite3,sys; s=sqlite3.connect(sys.argv[1]); d=sqlite3.connect(sys.argv[2]); s.backup(d); d.close(); s.close()" "$hist" "$bak2"

I did NOT replace my global stable installation.

Instead:

npx -y @openai/codex@0.154.0-alpha.3 --version

Output:

codex-cli 0.154.0-alpha.3

Then I opened the affected thread using that version:

npx -y @openai/codex@0.154.0-alpha.3 resume

The missing history immediately started showing up again.

The alpha was able to continue through both duplicated ordinal locations instead of aborting the complete projection.

  1. Verify that the complete rollout was consumed

Afterwards, my projection state was:

Projection state: (608475078, 92314)
Thread items: (27857, 92310)
Thread turns: (42, 92141)
Rollout bytes: 608475078
Bytes remaining: 0

The important part:

Projection byte offset: 608475078
Rollout file size: 608475078
Bytes remaining: 0

So the projection had successfully processed the entire 608 MB rollout.

Before recovery:

Thread items: 3
Thread turns: 1

After recovery:

Thread items: 27857
Thread turns: 42
11. Final result

I closed the CLI and reopened the normal Windows Codex/ChatGPT desktop app.

The full conversation history was back.

The original 608 MB rollout JSONL was never edited.

Root cause in my case

The failure chain was basically:

Long-running Codex thread
|
v
Rollout JSONL keeps growing normally
|
v
Projection cursor desync
expected ordinal 16
byte cursor already points to ordinal 17
|
v
Fix expected ordinal 16 -> 17
|
v
Projection continues scanning
|
v
Duplicate ordinal encountered
21963 -> 21963
|
v
Codex 0.153.4 aborts history projection
|
v
Run thread using 0.154.0-alpha.3
|
v
Duplicate/regressed ordinals no longer block the rebuild
|
v
Projection reaches EOF
|
v
Complete desktop conversation history restored

My rollout actually contained two duplicate ordinal sequences:

21963 token_count
21963 task_started
21964 world_state

and:

87104 token_count
87104 thread_settings_applied
87105 thread_settings_applied

So the data was not gone.

The broken part was the materialized/paginated thread_history projection.

WARNING

Please do not blindly run the SQL/Python update above using my numbers.

Values such as:

122207
16
17
21963
87104

were specific to my thread.

Before modifying anything:

Close all Codex/Desktop processes.
Back up the rollout JSONL.
Back up thread_history_1.sqlite using SQLite's backup API.
Verify the backup with PRAGMA integrity_check.
Inspect your own thread_history_projection_state.
Verify the actual JSONL record located at the stored byte offset.
Only correct the cursor if the mismatch is proven.
Do not modify the original rollout JSONL.

In my case, the only manual modification was correcting the initial projection cursor from ordinal 16 to 17.

I left both duplicate records untouched.

After that, running the thread once with 0.154.0-alpha.3 rebuilt the entire history successfully.

Hopefully this helps anyone else whose long-running Windows Codex thread suddenly appears to lose almost all of its history after restarting the app.

What steps can reproduce the bug?

It happens by itself

What is the expected behavior?

Recovering the Chat History

Additional information

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the thread-history projection state in thread_history_1.sqlite and the rollout JSONL records at the stored byte offsets; compare next_rollout_byte_offset and next_rollout_ordinal with the logged ordinal mismatches. Reproduce using the affected thread and inspect the projection logs around the initial cursor desync and duplicate ordinals. Done means the full rollout is consumed and the complete history appears in the desktop app without modifying the original JSONL.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, sqlite
Domain
databases, desktop
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.