Skip to content

Codex desktop writes high-frequency TRACE logs to ~/.codex/logs_2.sqlite despite RUST_LOG=warn #31111

Closed as duplicate

Description

@ballack311

Summary

Codex Desktop on macOS is continuously writing high-frequency TRACE-level logs into ~/.codex/logs_2.sqlite, including websocket/SSE frame-level logs. This creates sustained SQLite/WAL writes during normal app usage.

Environment

  • App: Codex Desktop for macOS
  • Version: 26.623.101652
  • Bundle build: previously observed 4674
  • macOS: 26.4.1 (25E253)
  • Date observed: 2026-07-04

Observed behavior

~/.codex/logs_2.sqlite receives a large volume of TRACE rows during normal interactive use. The related WAL file also remains active.

Recent measurement from the SQLite DB:

latest               last_30s_rows  last_30s_trace
2026-07-04 07:44:59  1079           1039

Recent process grouping:

process_uuid                                   rows  trace_rows  first_seen           last_seen
pid:4294:472d51df-7c2e-472f-9986-5e0b96eb7e4d  1200  1111        2026-07-04 07:43:01  2026-07-04 07:44:59

Earlier samples showed similar behavior across new app processes after restart, so this does not appear to be just a stale process.

Dominant log sources / content

Top TRACE sources observed include:

  • tokio-tungstenite
  • codex_app_server::outgoing_message
  • codex_mcp::connection_manager
  • codex_api::sse::responses

Example repeated message fragments include websocket/frame internals such as:

  • Opcode: Data(Text)
  • First: 11000001
  • Masked: false
  • WouldBlock
  • received frame
  • Parsed headers [...]
  • app-server event: item/agentMessage/delta targeted_connections=1

Attempted mitigation

Set macOS user launch environment:

RUST_LOG=warn

Confirmed via launchctl print gui/<uid> that RUST_LOG => warn was present. After restarting Codex, new processes still wrote TRACE rows to logs_2.sqlite, so this logging path appears not to be fully controlled by RUST_LOG.

Expected behavior

Normal Codex Desktop usage should not persist high-frequency TRACE websocket/SSE frame logs to ~/.codex/logs_2.sqlite unless an explicit diagnostic mode is enabled. A user-facing or documented way to disable this feedback/trace SQLite logging would also resolve the issue.

Impact

The app performs sustained local disk writes to SQLite/WAL files during normal use. This can cause unnecessary disk churn and makes ~/.codex/logs_2.sqlite grow with low-level TRACE entries.

Activity

  1. ballack311 commented on Jul 4, 2026

    @ballack311
    Author
  2. Sorkai commented on Jul 5, 2026

    @Sorkai

    I can reproduce the same issue on macOS with the current stable desktop build.

    Environment:

    • Codex Desktop: 26.623.101652
    • Bundle build: 4674
    • Bundled CLI: codex-cli 0.142.5
    • macOS: 26.5.2 (25F84), arm64
    • Writer process: /Applications/Codex.app/Contents/Resources/codex app-server --analytics-default-enabled
    • Local DB: ~/.codex/logs_2.sqlite

    Read-only sampling results. I did not create triggers, run checkpoint/VACUUM, delete rows, or modify the DB.

    Earlier 30s sample during normal interactive use:

    new_rows: 1234
    rows_per_sec: 41.1
    estimated_kib: 2984.59
    estimated_kib_per_sec: 99.49
    
    by level:
    TRACE 1201 97.3% 2569.92 KiB
    DEBUG   16  1.3%  202.56 KiB
    INFO    14  1.1%  175.19 KiB
    WARN     3  0.2%   36.92 KiB
    
    top TRACE targets:
    log                                  978
    codex_api::sse::responses            142
    codex_mcp::connection_manager         48
    codex_app_server::outgoing_message    30
    

    Fresh 20s follow-up sample:

    before_id: 88799455
    after_id:  88799966
    new_rows: 511
    rows_per_sec: 25.6
    estimated_kib: 140.02
    estimated_kib_per_sec: 7.0
    
    by level:
    TRACE 497 97.3% 134.70 KiB
    DEBUG  10  2.0%   3.63 KiB
    INFO    4  0.8%   1.69 KiB
    
    top TRACE targets:
    log                                           475
    hyper_util::client::legacy::pool               8
    hyper_util::client::legacy::client             6
    codex_app_server::outgoing_message             5
    hyper_util::client::legacy::connect::http      2
    codex_api::sse::responses                      1
    

    Representative repeated TRACE fragments include low-level websocket/SSE style entries such as tokio-tungstenite, WouldBlock, Opcode: Data(Text), and app-server delta events. I am intentionally not pasting raw feedback_log_body payloads because the SQLite log DB may contain private session text, local paths, or tool output.

    This looks like the same class of issue as described here: even on 26.623.101652 / 0.142.5, the persistent SQLite sink is still receiving high-volume TRACE rows, and the WAL/main DB mtimes continue to advance during normal use.

  3. Sorkai commented on Jul 5, 2026

    @Sorkai

    Additional in-app feedback ID for my reproduction above: 019f3132-544b-7a31-b6f6-485d4c9381b2

  4. Gloridust commented on Jul 8, 2026

    @Gloridust

    Root cause analysis, reproduced against current main (f1affbac5e), macOS.

    Two callsites feed the write volume:

    1. http-client/src/transport.rs:99,131 — the transport logs the full request body at TRACE on every model call. That body is the entire prompt context, tool outputs and images included.
    2. codex-api/src/sse/responses.rs:530 — the SSE parser logs every streamed event's payload at TRACE, one row per delta while a turn streams.

    Both sit behind enabled!(Level::TRACE), written on the assumption that TRACE is off in production. But the SQLite sink installed by both the TUI and the app-server (log_db::default_filter()) persists TRACE for all targets, so the guard always passes. This is also why RUST_LOG=warn doesn't help (#29463): RUST_LOG only filters the stderr layer; the sink has its own filter.

    Numbers from my ~/.codex: 87% of retained log content (33 of 38 MiB) is the transport target alone (codex_client::transport in the shipped build, codex_http_client::transport since #31323), with single rows up to 4 MiB. One such row eats most of the 10 MiB per-partition retention budget in a single write and evicts everything else — so this also degrades what /feedback can attach, and it means full prompt content sits unencrypted in logs_2.sqlite.

    Separate but related: the logs db is opened with auto_vacuum=INCREMENTAL, but nothing ever runs PRAGMA incremental_vacuum, so pages freed by retention/pruning stay on the freelist and the file never shrinks below its high-water mark. My logs_2.sqlite is 72 MB with 42% of its pages (30 MB) on the freelist.

    Fixes are small and follow the pattern of #29457 / #26396 / #29599:

    • cap codex_http_client::transport and codex_api::sse at DEBUG in log_db::default_filter(). With no sink enabling those targets at TRACE, the callsites are disabled outright, so the multi-MB serialization is skipped too. RUST_LOG=trace on stderr keeps working for local debugging.
    • run a capped PRAGMA incremental_vacuum(2048) from logs startup maintenance, between the retention delete and the passive checkpoint.

    Both implemented and tested (just test -p codex-state, 153 passed):

    Happy to open PRs if the team wants them.

  5. dreamitclean commented on Jul 17, 2026

    @dreamitclean

    I reproduced this on macOS 15.3.1 with ChatGPT Desktop 26.715.21425 / bundled codex-cli 0.145.0-alpha.18.

    As a temporary local mitigation (not an upstream fix), I tested a SQLite BEFORE INSERT trigger that drops only TRACE rows while retaining DEBUG, INFO, WARN, and ERROR.

    Before applying it:

    sqlite_sequence delta: +119 IDs / 10 s
    last 60 s: 936 TRACE rows, 3.82 MiB estimated
    

    After applying it during normal interactive use:

    sqlite_sequence delta: +13 IDs / 15 s
    new rows: INFO 7, DEBUG 5, WARN 1, TRACE 0
    codex process-reported bytes written: 352,256 / 15 s
    TRACE rows in a subsequent 5-minute check: 0
    

    A previous active-use sample on the same process was about 12.44 MiB / 20 s, so the observed write volume fell by roughly 96%. The app continued to operate normally in this session.

    Workaround

    Back up the live DB first using SQLite's backup API. Fully quitting Codex first is preferable, but the commands below also use a busy timeout:

    sqlite3 ~/.codex/logs_2.sqlite \
      ".timeout 10000" \
      ".backup '/private/tmp/codex-logs-before-trace-filter.sqlite'"

    Install the filter:

    sqlite3 ~/.codex/logs_2.sqlite \
      ".timeout 10000" \
      "BEGIN IMMEDIATE;
       CREATE TRIGGER IF NOT EXISTS codex_local_drop_trace_logs
       BEFORE INSERT ON logs
       WHEN NEW.level = 'TRACE'
       BEGIN
         SELECT RAISE(IGNORE);
       END;
       COMMIT;"

    Verify:

    sqlite3 ~/.codex/logs_2.sqlite \
      "SELECT type, name FROM sqlite_master
       WHERE type = 'trigger'
         AND name = 'codex_local_drop_trace_logs';"

    Rollback:

    sqlite3 ~/.codex/logs_2.sqlite \
      ".timeout 10000" \
      "DROP TRIGGER IF EXISTS codex_local_drop_trace_logs;"

    Caveats

    • This is an unofficial mitigation and may disappear if a migration/update recreates the logs table or database.
    • It intentionally removes TRACE diagnostics, so users should roll it back before collecting a diagnostic reproduction if maintainers request TRACE data.
    • It stops the SQLite row writes, but it does not prevent the application from constructing the TRACE event before the insert attempt. The proper upstream fix is still to filter the high-volume targets before serialization/enqueueing.
    • I did not delete old rows or VACUUM the live DB. Existing file size is separate from ongoing write churn, and VACUUM would cause a large one-time rewrite.
    • Avoid posting raw feedback_log_body contents; transport rows can contain prompt context, local paths, and tool output.

    This may be useful as a stopgap for affected users while the target-level filtering and incremental-vacuum fixes discussed above are reviewed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbugSomething isn't workingperformance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions