logs_2.sqlite-wal grows without bound into tens of GB
What version of Codex CLI is running?
codex-cli 0.140.0
codex doctor reports 0.141.0 is available, but this was observed on 0.140.0.
Also observed locally:
codex-cli 0.140.0
codex-cli 0.138.0 # codext wrapper
What platform is your computer?
Linux x86_64, Ubuntu 24.04 noble.
Runtime/install from codex doctor:
Codex Doctor v0.140.0 · linux-x86_64
runtime: npm
Node path: ~/.nvm/versions/node/v24.13.0/...
terminal: Alacritty
auth: ChatGPT auth
Which model were you using?
gpt-5.5
What issue are you seeing?
Codex SQLite log WAL files are growing without bound into many GB/tens of GB during normal local use. The main symptom is enormous logs_2.sqlite-wal files, while the base logs_2.sqlite database remains comparatively small.
This appears to be a default Codex log/state behavior, not something caused by a custom log setting. My setup uses multiple CODEX_HOME profile directories (~/.codex-work and ~/.codex-personal) in addition to the default ~/.codex, but that only relocates Codex state. The default config in ~/.codex/config.toml has not changed any log or SQLite settings. It only contains a trusted project entry. No log_dir or sqlite_home override is configured there.
The Codex docs say local state is stored under CODEX_HOME, defaulting to ~/.codex, and mention logs/caches as per-user state. They also document optional log_dir and sqlite_home settings. So this should affect a standard ~/.codex setup the same way; the profile dirs here just made the issue easier to isolate.
Concrete evidence
Earlier in this session, before cleanup:
~/.codex-work/logs_2.sqlite-wal 34,848,694,552 bytes
~/.codex-personal/logs_2.sqlite-wal 12,168,609,552 bytes
logs_2.sqlite-shm sidecars about 20-25 MB
logs_2.sqlite base DB about 115,650,560 bytes
After deleting/moving aside WAL/SHM sidecars and restarting Codex, the WALs reappeared and began growing again. A later check showed:
/home/<user>/.codex-personal/logs_2.sqlite 115,650,560 bytes
/home/<user>/.codex-personal/logs_2.sqlite-wal 1,693,608,432 bytes
/home/<user>/.codex-personal/logs_2.sqlite-shm 3,309,568 bytes
/home/<user>/.codex-work/logs_2.sqlite 115,650,560 bytes
/home/<user>/.codex-work/logs_2.sqlite-wal 19,184,359,792 bytes
/home/<user>/.codex-work/logs_2.sqlite-shm 37,257,216 bytes
/home/<user>/.codex/logs_2.sqlite 14,266,368 bytes
/home/<user>/.codex/logs_2.sqlite-wal 8,207,072 bytes
/home/<user>/.codex/logs_2.sqlite-shm 32,768 bytes
At the same time, profile directory sizes were dominated by these SQLite sidecars:
~/.codex-work 23G
~/.codex-personal 2.8G
~/.codex 104M
codex doctor reported the log DB itself as healthy:
state: databases healthy
log DB ~/.codex-personal/logs_2.sqlite (file) · integrity ok
But it did not warn about the enormous WAL file.
Impact
- Normal backup/rsync of Codex state attempted to copy tens of GB of volatile SQLite WAL files.
- The huge WAL files consumed significant disk space and made simple profile synchronization unexpectedly expensive.
- Deleting the WAL while Codex processes are live does not actually reclaim disk until those processes exit because the file remains open.
- Attempting to truncate open deleted WAL file descriptors caused active Codex instances to crash immediately.
- The current workaround is to exclude
*.sqlite-waland*.sqlite-shmfrom rsync/backup and only clean/checkpoint these files when Codex is not running.
Steps to reproduce
- Use Codex CLI/app normally for an extended local coding session, especially with substantial tool output.
- Inspect the Codex state directory:
``bash``
find "$CODEX_HOME" -maxdepth 1 \( -name '*sqlite-wal' -o -name '*sqlite-shm' -o -name 'logs_2.sqlite' \) -printf '%p %s bytes\n' | sort
- Compare
logs_2.sqlite-walsize withlogs_2.sqlite.
Expected result: WAL remains bounded, periodically checkpointed, or is truncated on clean shutdown.
Actual result: logs_2.sqlite-wal can grow into tens of GB while the base log DB remains around \~100 MB.
Expected behavior
- Codex should not allow
logs_2.sqlite-walto grow without a practical bound. - The logging/state layer should periodically checkpoint/truncate SQLite WAL files or configure WAL autocheckpointing appropriately.
- Large tool output should be capped, summarized, or rotated before it can accumulate unboundedly in persistent local log state.
codex doctorshould warn whenlogs_2.sqlite-walor other Codex SQLite sidecars exceed a reasonable threshold.- Ideally, Codex should expose documented retention/size controls for local logs and SQLite-backed runtime state.
Related issues
This may be related to, but is more specific than:
- openai/codex#27741, where a large
logs_2.sqlitecauses Desktop startup failures. - openai/codex#20563, which reports heavy I/O from idle Codex processes.
This issue is specifically about unbounded logs_2.sqlite-wal growth under default-style Codex state behavior.
11 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Correction to the original evidence section: the peak pre-cleanup observation was roughly 65 GB of SQLite log WAL files across the two active Codex profile/session folders. The smaller per-file numbers originally listed there were from a later post-cleanup/regrowth check and should not have been presented as the pre-cleanup peak. I edited the issue body to separate the peak observation from the later regrowth evidence.
Additional observation: a closed/stale Codex work-profile log WAL just measured at 219G / 234,157,858,816 bytes:
/home/<user>/.codex-work/logs_2.sqlite-wal. At that point the user's /home filesystem was full: 656G size, 623G used, 40M free, 100% used. The file was not held open by currently running Codex processes, so removing only the closed.codex-work/logs_2.sqlite-waland.codex-work/logs_2.sqlite-shmrestored free space to 219G available. Separately, currently running codext app-server/remotes were attached to.codex-personal/logs_2.sqlite-wal; three remote worker processes each showed roughly 170GB cumulative writes in /proc/<pid>/io while the active personal WAL was about 1.6G at inspection time. This confirms the issue can become disk-filling, not just inconvenient for backups.Follow-up DB-content scan from the surviving base log DBs found a likely self-amplifying source. The schema is a small log DB:
logs(id, ts, ts_nanos, level, target, feedback_log_body, module_path, file, line, thread_id, process_uuid, estimated_bytes), with WAL journaling enabled. The base DBs are only about 111 MB each, but they contain many TRACE rows where Codex logs file watcher events for its own log DB/WAL changes.\n\nExamples from.codex-work/logs_2.sqlite:\n\n- total logs rows: 42,534\n- TRACE rows: 38,537\n- rows withinotify event: 35,571\n- rows mentioninglogs_2.sqlite-wal: 26,401\n- repeated recent sample:inotify event: Event { wd: WatchDescriptor { id: 1, fd: (Weak) }, mask: EventMask(MODIFY), cookie: 0, name: Some(\"logs_2.sqlite-wal\") }\n\nExamples from.codex-personal/logs_2.sqlite:\n\n- total logs rows: 48,872\n- TRACE rows: 44,153\n- rows withinotify event: 38,580\n- rows mentioninglogs_2.sqlite-wal: 25,397\n- recent samples include repeatedinotify event ... name: Some(\"logs_2.sqlite\")rows.\n\nThis looks like a feedback loop: Codex logs that its own log DB/WAL changed; that insert changes the WAL again; the watcher observes another modify event; then another TRACE row is written. Because the DB is in WAL mode and checkpoint/truncation is not keeping the WAL bounded, a small 111 MB base DB can generate a 219 GBlogs_2.sqlite-wal. There are also large TRACE payload rows from websocket/frame logging, but the inotify self-logging loop is the clearest disk-filling mechanism found so far.Additional sanitized findings from a deeper investigation:
The strongest evidence now points to a self-watch / self-log loop around
$CODEX_HOME.Likely loop
logs_2.sqlite.logs_2.sqlite-wal.logs_2.sqlite-walmodified.TRACErow for that inotify event into the samelogs_2.sqlite.logs_2.sqlite-walagain.Important environment note
The newest evidence was collected while the affected session was being run through the
codextfork, which vendors/runs acodexbinary. That means this may be fork-specific, fork-amplified, or caused by an interaction between upstream Codex code andcodext's app-server/remote process model. I am posting this here because the DB/schema/logging/watch behavior is Codex-derived, but I am also filing the same sanitized report againstLoongphy/codext.Observed state
Sanitized paths:
The current post-cleanup WAL still grew back to roughly 12 GB:
Earlier growth samples during active sessions showed severe WAL growth:
This is after a previous stale/closed WAL reached 219 GB and filled the
/homefilesystem.SQLite log-table evidence
Counts from the affected
logs_2.sqlite:Top repeated inotify messages:
There are also unrelated file-open watcher events for system files such as
ld.so.cache,locale.alias, andpasswd, which appear to come from a separate/etcwatch. The disk-filling loop is the one involvinglogs_2.sqlite*.Kernel inotify evidence
The active affected process had an inotify file descriptor with:
The inode maps to
$CODEX_HOME:The repeated SQLite log rows also use
WatchDescriptor { id: 1, ... }, and the row names arelogs_2.sqlite-wal/logs_2.sqlite. That ties the logged file events directly to a watcher rooted at$CODEX_HOME, not just to a random project directory.Open file holders
The affected
codext/vendoredcodexprocess held open handles to:Other active
codextapp-server/remote processes held handles to a separate profile'slogs_2.sqlite-wal.Process model clue
The process tree included:
The exact project names have been omitted intentionally. Non-Codex dev-server processes running in one project did not hold handles to
logs_2.sqlite*; only Codex/codext processes did.Trust/root clue
The current Git repo root resolved correctly to a project directory under
/home/<user>/Projects/<repo>, but the UI reportedly displayed a trust warning for/home/<user>, not the project root. If a broad home directory becomes a trusted/watch surface, it can include$CODEX_HOMEand therefore Codex's own SQLite state.Even without that clue, the kernel inotify evidence above shows a watcher rooted at
/home/<user>/.codex-work.Relevant source areas
These upstream files look relevant:
Specifically:
file-watchercan watch requested paths and can fall back to the nearest existing ancestor.fs/watch.cwdif no Git root is resolved.Expected behavior
Codex should not log file watcher events for its own SQLite diagnostic/state files into the same SQLite log sink.
At minimum, file watcher trace logging should suppress:
Better fixes:
$CODEX_HOMEas a filesystem watch root unless explicitly required.logs_2.sqlite.Local mitigation being tested
The local mitigation is to move SQLite-backed runtime state outside the watched
CODEX_HOMEpath:This was added after the issue was observed. Already-running Codex/codext processes can continue holding the old
logs_2.sqlite*files until restarted.Cross-filed the sanitized fork-specific report against Loongphy/codext because the latest evidence was collected from codext/vendored-codex processes: https://github.com/Loongphy/codext/issues/11
This kind of program behavior is disrespectful in general, especially toward users with SSDs.
Follow-up after comparing the official OpenAI repo against the Codext fork and testing a fix:
I now think the concrete trigger I hit is not directly an OpenAI/Codex bug. The direct self-watch loop appears fork-specific in
Loongphy/codext: that fork adds anAuthWatchthat watches the top-levelCODEX_HOMEdirectory forauth.jsonchanges. Since the SQLite log DB normally also lives underCODEX_HOME, WAL writes can be observed by that watcher, and rawnotifyTRACE logs can be written back into the same SQLite log DB.Fork-specific issue:
https://github.com/Loongphy/codext/issues/11
Fix PR:
https://github.com/Loongphy/codext/pull/12
That said, I think the underlying risk is still present in OpenAI/Codex's implementation: the SQLite log sink persists TRACE logs by default, and the shared
log_db.rsdid not filter rawnotify/inotify compatibility logs. I did not find an official OpenAI/Codex production path that automatically watchesCODEX_HOMEitself, so the immediate trigger seems absent upstream. But if any current or future official watcher targets the SQLite directory, or a client asks app-serverfs/watchto watch that location, the same class of feedback loop could become user-visible.So this can probably be closed as "not directly reproduced in upstream OpenAI/Codex" if maintainers agree, but I would still recommend hardening the SQLite log sink so it does not persist raw file-watcher backend TRACE/DEBUG noise in a very damaging leaking fs-notify loop.
Potentially a duplicate of / related to:
Which should have been fixed by:
Which were released in:
But there still seem to be issues after that:
Fix bounds the WAL:
journal_size_limit=16MiB+wal_autocheckpoint=400, plus a periodic best-effortTRUNCATEcheckpoint (previously only a startup PASSIVE checkpoint): https://github.com/denispol/codex/pull/1I hit these but my sqlite db size was genuinely nutty 😆