Desktop (macOS): "failed to initialize sqlite state runtime" when another Codex app-server holds logs_2.sqlite (not corruption, not size)
Summary
On native macOS, Codex Desktop fails to launch with the blocking dialog "Codex cannot access its local database" / failed to initialize sqlite state runtime under ~/.codex. The trigger is concurrent access: a second Codex app-server (a Codex IDE extension, an older build) was holding ~/.codex/logs_2.sqlite open, which prevented the Desktop app-server from initializing the SQLite state runtime within the startup handshake.
Both state_5.sqlite and logs_2.sqlite pass PRAGMA integrity_check (ok) and initialize fine in isolation — so this is neither corruption nor (purely) size. This is likely the same underlying cause as #27741 and #28666, but observed on native macOS and pinpointing multi-client / shared-CODEX_HOME concurrency as the trigger.
Environment
- Codex Desktop for macOS, version 26.623.30605 (build 4415)
- Bundled
codex-cli 0.142.2 - macOS 26.5.1, Apple Silicon (arm64)
- Default
~/.codexon the internal APFS volume (not WSL, no/mnt/network mount) - Several Codex clients run against the same
~/.codex(Desktop app, Codex IDE extensions, and thecodexCLI), each spawning its own ephemeralapp-server(the app-server daemon is not enabled)
User-visible failure
Codex cannot access its local database.
...
Error: (code=1, signal=null).
Most recent error: Error: failed to initialize sqlite state runtime under
~/.codex: failed to initialize state runtime at ~/.codex
Desktop logs show the handshake child exiting code=1 repeatedly, ~5–8 s per attempt:
initialize_handshake_result durationMs=7308 ... outcome=failure
reason="Error: failed to initialize sqlite state runtime under ~/.codex: failed to initialize state runtime at ~/.codex"
One internal tracing line surfaced during a failed init: WARN ... slow statement: execution time exceeded alert threshold.
Root cause (reproduced)
lsof ~/.codex/logs_2.sqliteshowed another Codexapp-server(a Codex IDE extension's, an older build) holding it (+-wal,-shm) open continuously.state_5.sqlitehad no holders.- Driving the bundled app-server directly reproduces it:
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}\n' | CODEX_HOME=~/.codex codex app-server → exits 1 with the same error.
- Bisection (all on
codex-cli 0.142.2):
- Fresh empty
CODEX_HOME→ init succeeds - Fresh home + real
state_5.sqlite(170 MB) → succeeds - Fresh home + real
logs_2.sqlite(1.14 GB) → succeeds (init < 10 s) - All four real DBs together + real
config.toml→ succeeds - Real
~/.codexwhile another app-server holdslogs_2.sqlite→ fails - Kill that app-server (freeing
logs_2.sqlite), retry against real~/.codex→ succeeds (durationMs ≈ 450)
PRAGMA integrity_checkon bothstate_5.sqliteandlogs_2.sqlite=ok.
So the state DBs are healthy; the Desktop app-server simply cannot acquire the access it needs to logs_2.sqlite during init while another (often older-version) app-server holds it, and the init aborts within the startup handshake budget.
Impact
Desktop refuses to launch entirely whenever another Codex client (IDE extension / CLI / another app-server) happens to be running against the same ~/.codex. The dialog advises "Close other Codex applications," but it is not obvious that a background IDE-extension app-server counts, and the heavier "Back Up and Rebuild" is unnecessary because the DB is fine.
Workaround
Free logs_2.sqlite instead of rebuilding:
lsof ~/.codex/logs_2.sqlite # find the holding app-server PID(s)
kill <pid> # stop them
# relaunch Desktop
No data is moved or lost.
Suggestions
- Tolerate concurrent app-servers. The state runtime should coexist with other app-servers (shared WAL readers, or coordinate via the app-server daemon / single-writer with graceful waiting) instead of aborting the Desktop handshake.
- Surface the leaf cause. On macOS the error collapsed to a generic "failed to initialize state runtime at ~/.codex" with no underlying SQLite error (contrast #28666, which shows
(code: 5) database is locked). Including the underlying SQLite/sqlxerror (busy / locked / pool timeout) in the dialog and logs would make this self-diagnosable. - Targeted recovery. When only
logs_2.sqliteis the blocker, offer a "retry after releasing the logs DB" / recreate-just-the-logs-DB path (cf. #24001) rather than the full Back Up and Rebuild.
Related: #27741, #28666, #24006, #24001, #21750.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗