Excessive SQLite WAL writes during streaming due to TRACE logs ignoring RUST_LOG
What version of the IDE extension are you using?
openai.chatgpt-26.406.31014-linux-x64 (VSCodium on Linux Mint)
What subscription do you have?
ChatGPT Plus
Which IDE are you using?
VSCodium (Code - OSS fork)
What platform is your computer?
Linux x64 (Linux Mint)
What issue are you seeing?
During model streaming responses, the Codex app-server binary writes ~5 MiB/s (observed up to ~16 MiB/s in iotop) to ~/.codex/logs_2.sqlite-wal. This results in sustained disk write rates that can significantly impact SSD longevity and system performance.
This issue is independent of the thread-stream-state-changed warning spam and the open-in-targets retry loop, which were investigated separately.
Root cause
The extension correctly sets RUST_LOG=warn when spawning the app-server process — confirmed via /proc/<pid>/environ:
RUST_LOG=warn
However, the SQLite logging sink appears to bypass or not respect the RUST_LOG filter, since TRACE-level entries are still persisted to logs_2.sqlite despite RUST_LOG=warn. The SQLite logging path appears to operate outside of the standard RUST_LOG filtering mechanism, or uses a separate filter configuration.
This was confirmed via strace during streaming:
pwrite64(20</home/k/.codex/logs_2.sqlite-wal>, ..., 4096, ...) = 4096
pwrite64(20</home/k/.codex/logs_2.sqlite-wal>, ..., 4096, ...) = 4096
...
And via SQLite query — MAX(id) jumped by ~5000 rows for a single short (~50 token) response, while total row count remained stable, indicating that rows are being rapidly inserted and pruned/rotated during streaming:
-- Before response:
SELECT MAX(id), COUNT(*) FROM logs; --> 3088667 | 30141
-- After response:
SELECT MAX(id), COUNT(*) FROM logs; --> 3093673 | 30141
Log level breakdown in logs_2.sqlite:
TRACE 20485 (68%)
INFO 8286
DEBUG 1326
WARN 44
Sample TRACE entries written during streaming:
target=hyper_util::client::legacy::pool feedback_log_body="idle interval checking for expired"
target=log feedback_log_body="WouldBlock"
target=log feedback_log_body="/home/runner/work/codex/codex/.cargo-home/git/checkouts/tokio-tungstenite-..."
These are low-level internal network events from tokio-tungstenite and hyper_util with no diagnostic value for end users. They should not be persisted to disk in a production binary — and critically, they are not suppressed by RUST_LOG=warn as one would expect.
What steps can reproduce the bug?
- Open a workspace in VSCodium/VS Code with the Codex extension.
- Confirm
RUST_LOG=warnis set:
cat /proc/$(pgrep -f 'codex.*app-server')/environ | tr '\0' '\n' | grep RUST_LOG
- Run
strace -f -e trace=pwrite64 -p $(pgrep -f 'codex.*app-server')in a separate terminal. - Ask any question in the Codex chat panel and observe streaming.
- Observe continuous
pwrite64calls tologs_2.sqlite-walat ~5 MiB/s despiteRUST_LOG=warn.
Alternatively, check log level distribution after a session:
sqlite3 ~/.codex/logs_2.sqlite "SELECT level, COUNT(*) FROM logs GROUP BY level ORDER BY COUNT(*) DESC"
What is the expected behavior?
RUST_LOG=warn should suppress TRACE and DEBUG output everywhere, including the SQLite logging sink. The current behavior — where RUST_LOG=warn suppresses stderr output but does not affect SQLite persistence — is inconsistent and undocumented.
TRACE-level events from internal networking libraries (tokio-tungstenite, hyper_util) should never be written to the persistent SQLite log database during normal operation.
Additional information
Suggested fix
The SQLite logging sink in app-server should apply the same log-level filter as RUST_LOG (or the internal tracing subscriber filter), rather than maintaining a separate or hardcoded verbosity level. The fix should ensure that setting RUST_LOG=warn — as the extension already does — is sufficient to suppress TRACE/DEBUG writes to logs_2.sqlite.
Workaround
Users can redirect logs_2.sqlite to tmpfs to protect their SSD while waiting for a fix:
# stop VSCodium first
mv ~/.codex/logs_2.sqlite ~/.codex/logs_2.sqlite.bak
ln -s /tmp/logs_2.sqlite ~/.codex/logs_2.sqlite
Note: logs_2.sqlite contains only internal diagnostic logs — no conversation history — so data loss on reboot is acceptable.
After symlinking logs_2.sqlite to tmpfs, disk I/O from the Codex extension dropped to zero during streaming, confirming that the SQLite WAL is the sole source of the observed write load.
Workaround 2: block inserts into the SQLite log table
Another workaround was suggested by @synap5e in this comment:
https://github.com/openai/codex/issues/17320#issuecomment-4514992998
Instead of redirecting logs_2.sqlite to tmpfs, users can prevent new rows from being inserted into the logs table by adding a SQLite trigger:
sqlite3 ~/.codex/logs_2.sqlite "CREATE TRIGGER IF NOT EXISTS block_log_inserts BEFORE INSERT ON logs BEGIN SELECT RAISE(IGNORE); END;"
This causes attempted inserts into the logs table to be ignored without returning an error to the application, which should prevent logs_2.sqlite-wal from growing due to persistent TRACE/DEBUG log writes.
To remove the workaround:
sqlite3 ~/.codex/logs_2.sqlite "DROP TRIGGER IF EXISTS block_log_inserts;"
As with the tmpfs workaround, this is not a proper fix. It disables local persistent logging rather than fixing the underlying issue: the SQLite logging sink appears to ignore RUST_LOG=warn and continues persisting TRACE/DEBUG entries during normal streaming.
18 Comments
I haven't dug into this issue with the same depth as you have, but I started noticing HUGE swings in my disk use over the past month (like 60GB less by end of a single day just doing light-weight work with codex). I eventually realized the "Code Helper" under "disk" within the Activity Monitor on MacOS was showing insane amounts of data being written to disk. This was happening even when I wasn't actively using VSCode (just remained open from past completed Codex work). In my particular case, even after closing VSCode and restarting, the disk space wasn't reclaimed.
It took some extra time to realize this bug was perfectly paired with the default setting of Time Machine holding onto local hourly snapshots on disk so I went ahead and did a backup, manually deleted all local snapshots, and set Time Machine to manual so as to avoid compounding this particular Codex bug.
Given its current state, I am literally going to stop using Codex for now, as it aggressively writes to disk all the time and there's nothing I can do about it. Apologies if what I've described isn't an exact match, but given the overlap of the extension writing to disk at aggressive rates, it felt related.
I also see this behavior. Do you still have this problem? Could you fix it by symlinking to a tmpfs? Just linking
logs_2.sqliteto tmpfs didn't solve the apparent disk writes for me (unless Linux reports writes to tmpfs equally??)I searched for similar issues and there are mutliple, but no reaction at all from the devs yet. These issues are a month old already 😬
It's a workaround but yes, it works. Just make sure that your /tmp is using RAM. Some distros have it set to HDD/SDD.
Thanks. I ended up just symlinking the whole
~/.codexfolder (and copying it back before shutdown, hopefully...). /tmp is indeed a tmpfs for me.I also found this while investigating Codex's CPU usage, writing these trace level logs burns a lot of CPU. A way to reduce the logging level should be added to speed up Codex on older devices.
I tested this and it helps a lot, but there's some more CPU savings from disabling the logging fully.
Each Codex instance was prompted to edit a json file.
Mode | Log rows | Log DB size | time CPU | perf user task-clock | SQLite worker
-- | -- | -- | -- | -- | --
Full inserts | 1888 | ~3.8 MB | 3.64s | 2.394s | 0.657s
Insert ignored | 0 | ~40 KB | 2.42s | 1.576s | 0.061s
Fully disabled | 0 | ~40 KB | 2.01s | 1.061s | 0 samples
how did you do that?
I patched the codex source to remove the log db's initialization.
<details>
<summary>Patch: disable log DB initialization</summary>
</details>
Useful workarounds here when using Codex on a Raspberry Pi with SD card storage, thanks!
This kind of program behavior is disrespectful in general, especially toward users with SSDs.
There's already a shortage of SSDs, plz fix.
I can reproduce this on Windows Desktop.
Environment:
OpenAI.Codex26.616.6631.0codex-cli 0.128.0Microsoft Windows NT 10.0.26200.0RUST_LOG=warn,LOG_LEVEL=warnconfig.toml:[analytics] enabled = falseBefore workaround:
logs_2.sqlitehad grown to about 2.0 GB, with about 1.47 GB of freelist bloat.TRACElogINFOcodex_otel.log_onlyINFOcodex_otel.trace_safeTRACEcodex_api::endpoint::responses_websocketTRACEcodex_api::sse::responsesTRACE/DEBUGtargets as wellgit.exestorm was present during this sample.Workaround tested:
Result:
logsrows.INFO/WARNrows, roughly 30-40 rows/min in an active session.The SQLite sink needs to respect an effective persisted-log filter, not only the process RUST_LOG. I would log one startup row showing requested filter, effective DB filter, targets enabled, and whether transport payload logging is on. Streaming diagnostics should be durable as counters and error events, while raw TRACE frames stay off unless explicitly enabled with a byte cap.
---
_Generated with ax._
I made a small unofficial tool for running community-found Codex workarounds:
https://codexfixes.com
Run it with:
So we can stop checking issue threads every day for months and just patch the annoying stuff when the community finds a workaround.
Reproduced on Windows Codex Desktop on 2026-06-29 00:40:10 CEDT +0200.
RUST_LOG=warn was set.
~/.codex/logs_2.sqlite was 689,213,440 bytes.
logs_2.sqlite-wal was 5,500,232 bytes.
logs table had ~207k rows, mostly TRACE.
After blocking inserts + VACUUM + WAL truncate, DB dropped to 40,960 bytes and WAL to 0.
Windows Desktop confirmation, same failure mode on Windows.
RUST_LOG=warnwas configured in both.codex/.envand the user environment.id_delta=229withcount_delta=0, and another sample grew the WAL by 26,199,080 bytes (~25.0 MiB).rmcp::service,codex_api::sse::responses, andhyper_util.I then applied the community
block_log_insertsBEFORE INSERT trigger. With Codex still running, a fresh 15-second read-only sample showedid_delta=0,count_delta=0, andwal_delta_bytes=0.This is an effective local stopgap but disables persistent local diagnostics. The upstream fix should still make the SQLite sink honor
RUST_LOGor avoid persisting high-volume SSE/WebSocket TRACE events by default.macOS confirmation with local quantitative evidence.
Environment:
codex-cli 0.145.0-alpha.18~/.codex/logs_2.sqliteHistorical snapshot before the insert-blocking workaround took effect:
logs_2.sqlite: 223,064,064 bytes (~212.7 MiB)estimated_bytes: 147,795,856sqlite_sequenceforlogs: 11,634,061The large sequence value relative to retained rows is consistent with substantial insert-and-prune churn. The database file remaining around 213 MiB therefore does not reflect cumulative physical writes.
The community
block_log_insertstrigger is currently installed. During active Desktop streaming, read-only samples showed:sqlite_sequenceThis supports the trigger being an effective stopgap on macOS as well, but it disables persistent diagnostics and is not an acceptable long-term product fix.
Please prioritize an upstream fix that:
All measurements above were collected read-only; no VACUUM, checkpoint, deletion, or database rebuild was performed during verification.