state runtime: corrupt `state_5.sqlite` (SQLite "file is not a database") wedges startup with no auto-recovery

Open 💬 19 comments Opened May 8, 2026 by MrPrezident
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex CLI is running?

sha: 5ecff051962e7299c743e4ce9c1545d71b756924

What subscription do you have?

Enterprise

Which model were you using?

gpt-5.5

What platform is your computer?

Linux 5.14.21-150400.24.184-default x86_64 x86_64

What terminal emulator and version are you using (if applicable)?

mate-terminal

What issue are you seeing?

## Summary

When ~/.codex/state_5.sqlite (or its companion logs_2.sqlite) becomes unreadable as a SQLite database — e.g. truncated, partially written, or replaced with non-SQLite content — StateRuntime::init fails to open the pool with SQLite extended error code 26 / "file is not a database", and the failure surfaces as:

``
failed to initialize state runtime at <codex_home>: error returned from database: (code: 26) file is not a database
``

The user data on disk (history.jsonl, sessions/**/rollout-*.jsonl, external_agent_session_imports.json) is intact, and Codex is designed to be able to rebuild thread metadata from JSONL via the rollout backfill path. But because open_state_sqlite / open_logs_sqlite propagate the open error unconditionally, the agent cannot make forward progress on startup until the user manually moves the corrupt DB aside.

This is the "Expected behavior" already requested in #20493:

> If state_5.sqlite migration/open fails, Desktop should detect the corruption, preserve the DB, rebuild from JSONL, and show a visible recovery/warning state instead of looking like the user's chats are gone.

I'd like to extend that ask to the CLI/state runtime layer as well, since the same DB and the same open path are shared.

## Affected code

  • codex-rs/state/src/runtime.rs
  • StateRuntime::init -> open_state_sqlite -> SqlitePoolOptions::connect_with (returns sqlx::Error::Database with code 26)
  • StateRuntime::init -> open_logs_sqlite (same shape)
  • codex-rs/rollout/src/state_db.rs
  • init swallows the error and emits a startup warning, but the DB is then None for the rest of the process. Consumers that still expect a usable DB (e.g. embedded app-server thread state) operate in a degraded state.

PR #21481 ("Revert state DB injection and agent graph store") helpfully restored Option<StateDbHandle> through several call sites, which softens the blast radius. It does not, however, recover from corruption — the user still needs to manually quarantine the file before Codex can rebuild.

## Why this happens in practice

I have seen this on Linux after an aborted/forced shutdown left state_5.sqlite truncated to ~0 bytes with stale -wal/-shm sidecars. #20493 reports the same shape on macOS Desktop after the import flow, where the file ends up as generic data rather than a SQLite header. Because the DB is rebuildable from JSONL, the failure mode is recoverable in principle — it just needs a code path that does the rebuild.

## Proposed fix

At the SQLite open boundary in codex-rs/state/src/runtime.rs, detect SQLite extended code 26 / "file is not a database" specifically, quarantine the file (and its -wal, -shm, -journal sidecars) with a .corrupt-<UTC timestamp> suffix, and retry the open so migrations recreate a fresh schema. The rollout backfill path then rebuilds thread metadata as it normally would.

Important constraints:

  • Do not auto-quarantine on migration errors, permission errors, or lock errors. Those are not corruption and should continue to surface as real failures.
  • Do not delete the corrupt file. Rename with a timestamped suffix so users can keep it for forensics or send it in if asked.
  • Apply the same recovery to both state_5.sqlite and logs_2.sqlite, since both go through symmetric helpers.

Sketch of the change in runtime.rs (only the recovery wrapper shown; the body is straightforward):

```rust
async fn open_state_sqlite_recovering(
path: &Path,
migrator: &Migrator,
) -> anyhow::Result<SqlitePool> {
match open_state_sqlite(path, migrator).await {
Ok(pool) => Ok(pool),
Err(err) if is_sqlite_not_database_error(&err) => {
warn!(
"state db at {} is not a valid sqlite database; quarantining and recreating it",
path.display()
);
quarantine_sqlite_files(path, "state").await?;
open_state_sqlite(path, migrator).await
}
Err(err) => Err(err),
}
}

fn is_sqlite_not_database_error(err: &anyhow::Error) -> bool {
err.chain().any(|cause| {
let Some(sqlx_err) = cause.downcast_ref::<sqlx::Error>() else {
return false;
};
match sqlx_err {
sqlx::Error::Database(db_err) => {
db_err.code().as_deref() == Some("26")
|| db_err.message().contains("file is not a database")
}
_ => sqlx_err.to_string().contains("file is not a database"),
}
})
}
```

A regression test that writes garbage to state_5.sqlite, calls StateRuntime::init, and then asserts that:

  • init returns Ok(_),
  • the schema is present (e.g. get_backfill_state returns Pending),
  • the original file has been preserved with a .corrupt-<UTC> suffix,

is enough to lock the behavior in place.

What steps can reproduce the bug?

```bash
# Pick any codex_home with prior session activity.
CODEX_HOME=$(mktemp -d)
mkdir -p "$CODEX_HOME"
printf 'not a sqlite database' > "$CODEX_HOME/state_5.sqlite"

# Start any path that calls StateRuntime::init, e.g. codex (TUI).
CODEX_HOME="$CODEX_HOME" codex
# -> "failed to initialize state runtime at <CODEX_HOME>: error returned from database: (code: 26) file is not a database"
```

A unit test reproduces the same condition without needing the full TUI:

```rust
let codex_home = unique_temp_dir();
tokio::fs::create_dir_all(&codex_home).await?;
let state_path = state_db_path(codex_home.as_path());
tokio::fs::write(&state_path, b"not a sqlite database").await?;

// Today: fails with code 26 and the runtime cannot start.
let _ = StateRuntime::init(codex_home.clone(), "test-provider".to_string()).await?;
```

What is the expected behavior?

  1. On any path that calls StateRuntime::init, a corrupt SQLite state or logs DB no longer blocks startup.
  2. The corrupt file is preserved as <name>.corrupt-<UTC timestamp> next to the original, with -wal, -shm, -journal sidecars also moved aside.
  3. A fresh DB is created via the existing migrator; rollout backfill rebuilds thread metadata from JSONL.
  4. Other classes of open failure (migration, permission denied, lock contention) still surface as errors, unchanged.

Additional information

## Alternatives considered

  • Front the open with a SQLite header sniff (read first 16 bytes, check "SQLite format 3\0"). Slightly cleaner error semantics, but only catches the truncated/garbage case. Corruption past the header still surfaces as code 26, so the chain-inspection path is still needed. I'd keep the chain-inspection design.
  • Keep recovery in callers (TUI, app-server, etc.). This duplicates logic and is racy across processes that share CODEX_HOME. The single chokepoint at StateRuntime::init covers every consumer, including future ones.
  • Always rebuild on any open error. Too broad. Hides real migration/permission/lock failures. Restricting to SQLite code 26 keeps the recovery scoped to corruption-shaped failures.

View original on GitHub ↗

19 Comments

github-actions[bot] contributor · 2 months ago

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

  • #20493

Powered by Codex Action

ErwinCell · 2 months ago

I've encountered the same problem. How can I fix it?

nasa1024 · 2 months ago

<img width="833" height="467" alt="Image" src="https://github.com/user-attachments/assets/773b089f-c50f-4456-855f-526ce0cd0fde" />
bump in 21/05/2026
platform:windows11
when i delete the state 5.sqlite codex app couldn't run

Thesmit113 · 2 months ago

<img width="573" height="307" alt="Image" src="https://github.com/user-attachments/assets/12565a9f-05d6-41a7-b3e4-61062d6641a6" />

I updated Codex today and am getting this error. I tried deleting state_5.sqlite and restarting Codex, but I am still getting the same error.

ShreyBosamia · 2 months ago

<img width="562" height="303" alt="Image" src="https://github.com/user-attachments/assets/2412596c-3ebf-4eaa-871a-bb310179f387" />

I updated Codex today as well and got the same error as well.

shivam-rawal · 2 months ago

<img width="824" height="462" alt="Image" src="https://github.com/user-attachments/assets/c0cde637-a373-493c-94dc-5f907c4cf960" />

Ah damn I updated Codex right now and got the same error. How can this be fixed!!

anagounagi · 2 months ago

Windows/WSL conflict error. Make sure to right-click Codex in the system tray and select "Exit" first.

Press Win + R → Open %USERPROFILE% and find the .codex folder.

Rename state_5.sqlite inside the folder and launch the app.

If it fails, rename the entire .codex folder (e.g., .codex_bak) and launch the app (Full reset).

After launching, move your necessary files/folders back from the old folder into the newly created .codex folder.

B3n01tH · 2 months ago

Having the same problem here, tried the workaround above but as soon as you restart codex it fails again with the same error so if you do that you can't just restart codex anymore.

A fix is required now, we can't just wait as enterprise users !!

yiminzme · 2 months ago

+1

B3n01tH · 2 months ago

This is only happening for WSL just tried a new profile (empty .codex) Using the PowerShell agent and that works

EvGenStrike · 2 months ago

Is there a permanent fix?

<img width="543" height="294" alt="Image" src="https://github.com/user-attachments/assets/71de1507-6db0-495a-9ea3-063afbcd40e6" />

ekil1100 · 2 months ago

this happens when I choose wsl to run agent, use windows to run agent is fine

al-imam · 2 months ago

How to fix it

ChouUn · 2 months ago
xdifu · 2 months ago

Small clarification for people landing here from the May 21 Windows/WSL startup crash: there are two different SQLite failures being mixed in nearby reports.

The original issue here is true SQLite corruption (file is not a database, code 26). My repair tool is not meant for that case.

If your dialog instead says Codex cannot access its local database and the most recent error is migration 1 was previously applied but has been modified, that is the CRLF/LF SQLx checksum issue tracked in #23777 / #23787. For that specific case, I published a non-destructive recovery tool here: https://github.com/xdifu/codex-repair

Windows PowerShell / Windows Terminal:

git clone https://github.com/xdifu/codex-repair.git
cd codex-repair
python codex-repair.py doctor
python codex-repair.py fix --apply

If you run it from WSL bash to repair Windows Codex Desktop data, pass --codex-home "/mnt/c/Users/<WindowsUser>/.codex" so it does not target WSL's own ~/.codex.

It backs up state_5.sqlite / logs_2.sqlite, schema-verifies before rewriting _sqlx_migrations, and leaves ~/.codex/sessions/*.jsonl read-only.

ErwinCell · 2 months ago
我也遇到了同样的问题。请问如何解决?

Solved. Steps:

  1. Completely close the Codex client. You can try ending the process through Task Manager.
  2. Locate the following files in C:\Users\<user>\.codex:

"state_5.sqlite",
"state_5.sqlite-wal",
"state_5.sqlite-shm",
"logs_2.sqlite",
"logs_2.sqlite-wal",
"logs_2.sqlite-shm"

  1. Manually change the file extension to ".broken".

tips: These are also things that chatgpt taught me. : )

al-imam · 2 months ago
> How to fix it #23841 (comment) it works!

thank you but i deleted codex folder and reauthenticated 😑

I don't needed old chat that much so easier option

0xFlicker · 2 months ago

Fresh windows install. Move %HOME%/.codex out of the way. Log in. Opens fine.

Switch from Windows Native to -> WSL. Restart requested.

Restart. 💥

<img width="556" height="298" alt="Image" src="https://github.com/user-attachments/assets/137fd858-10a0-4d3f-a16f-c08d3d2ae72a" />

cocovs · 2 months ago

I hit the same startup wedge, but with a different SQLite corruption shape.

Environment:

  • macOS Desktop
  • Codex.app version: 26.519.22136
  • Affected DB: ~/.codex/state_5.sqlite
  • Startup error:

while executing migration 25: error returned from database: (code: 11) database disk image is malformed

pragma integrity_check; on the quarantined DB reports:

btreeInitPage() returns error code 11
wrong # of entries in index idx_threads_provider
wrong # of entries in index idx_threads_source
wrong # of entries in index idx_threads_archived
wrong # of entries in index idx_threads_updated_at
wrong # of entries in index idx_threads_created_at
wrong # of entries in index sqlite_autoindex_threads_1
Error: stepping, database disk image is malformed (11)

This differs from the original report's SQLite code 26 / file is not a database: the file still has a valid SQLite header, but internal B-tree/index pages are corrupt.

Manual recovery was the same:

  • quit/kill Codex
  • move state_5.sqlite, state_5.sqlite-wal, state_5.sqlite-shm, and any journal aside with .corrupt-<UTC>
  • restart Codex
  • a fresh DB was created and startup succeeded

Related observations from local forensics:

  • logs_2.sqlite.corrupt-* existed but was 0 bytes in the preserved evidence, so I cannot prove logs DB corruption from that copy.
  • session_index.jsonl was valid JSONL and not obviously corrupt.
  • no nearby reboot, shutdown, disk space exhaustion, APFS/I/O error, or Codex crash report found.
  • there was an app update/replacement around the same startup window, and startup failed immediately afterward during migration 25.

Request: could the proposed recovery also cover SQLite corruption code 11 / SQLITE_CORRUPT / database disk image is malformed, not only code 26 / file is not a database?

I agree recovery should stay narrow and preserve the original files rather than deleting them. I can provide the quarantined DB privately if maintainers want it.