Skip to content

Excessive SQLite WAL writes during streaming due to TRACE logs ignoring RUST_LOG #17320

Description

@piotrkacala

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?

  1. Open a workspace in VSCodium/VS Code with the Codex extension.
  2. Confirm RUST_LOG=warn is set:
cat /proc/$(pgrep -f 'codex.*app-server')/environ | tr '\0' '\n' | grep RUST_LOG
  1. Run strace -f -e trace=pwrite64 -p $(pgrep -f 'codex.*app-server') in a separate terminal.
  2. Ask any question in the Codex chat panel and observe streaming.
  3. Observe continuous pwrite64 calls to logs_2.sqlite-wal at ~5 MiB/s despite RUST_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:
#17320 (comment)

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentIssues related to the core agent loopbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions