openai / openai/codex

config/batchWrite on startup rewrites config.toml and drops all unmanaged keys

Open
#42,116 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app app-server bug
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of the Codex App are you using?

26.825.51511

What subscription do you have?

Pro

What platform is your computer?

Darwin 25.6.0 arm64 arm

What issue are you seeing?

A config/batchWrite during app startup rewrote ~/.codex/config.toml and dropped every
key the app does not manage. The file went from 12,338 bytes to 2,171.

Three things survived: the top-level notify key, all [plugins.*] tables, and the three
app-owned MCP servers (node_repl, computer-use, cua_repl).

Everything else was deleted. The inventory below comes from a copy of the file taken 9
days before the truncation, which is the freshest copy I could recover. The live file had
grown by about 678 bytes since then, so the real loss is slightly larger than this:

  • top-level model, model_provider, model_reasoning_effort, approval_policy,
    approvals_reviewer, personality, service_tier, default_permissions, and both
    experimental_realtime_*_base_url keys
  • [features], [realtime], [model_providers.*], [marketplaces.*]
  • 8 user-defined [mcp_servers.*] entries
  • [shell_environment_policy], [desktop], [hooks.state.*], [skills], [tui],
    [notice], [apps.*], and [projects.*] with 29 entries
  • 10 of the 15 [plugins.*] entries

The file stayed valid TOML, so nothing failed loudly. The first symptom was threads
refusing to resume:

ChatGPT can't load config.toml, so this thread can't resume.
Fix config.toml: Model provider `codex-lb` not found. After saving the file, reopen the thread.

Every existing thread records the provider it ran on, so deleting [model_providers.*]
strands all of them at once.

What steps can reproduce the bug?

I can't reproduce it on demand. Sequence from ~/.codex/logs_2.sqlite, in UTC:

13:34:09   previous app-server (pid 25782, running since Aug 30) writes its last log line
14:04:10   new app-server (pid 410) starts, client_name="Codex Desktop" 26.825.51511
14:04:13   experimentalFeature/enablement/set
           WARN codex_app_server::request_processors::config_processor
           ignoring invalid experimental feature enablement keys: apps_mcp_path_override
14:04:16   config/batchWrite
           request_id "config/batchWrite:79b01df7-5657-4996-9939-71118c2deb0f"
14:04:16   config.toml mtime, size now 2,171 bytes
14:04:29   pid 410 writes its last log line

That app-server lived 19 seconds. It started, rewrote the config, and exited.

Four observations that may narrow it down:

  1. config/batchWrite ran 10 times between Aug 22 and Aug 30 (Aug 22, 26, 28 x2, 29 x3,
    30 x2) and preserved the file every time.
  2. The next launch an hour later, at 15:04:20, issued no config/batchWrite at all and
    left the config intact. The write is not part of every startup.
  3. The apps_mcp_path_override warning is probably a red herring. It shows up on Aug 22,
    25, 28 and 30 too, with no data loss.
  4. No WARN or ERROR appears in the four minutes before the truncating startup.

The only odd thing I can find is that 19 second lifetime. My guess is that batchWrite
serialized a config model that hadn't finished loading. Keys the model didn't hold got
written out as absent instead of left alone.

What is the expected behavior?

config/batchWrite should merge into the existing file and leave keys it does not own
untouched, which is what it does on every other run.

I'd also like this to be structurally impossible, not just rare. Either of these would
have saved my config:

  • copy config.toml to config.toml.bak before replacing it
  • refuse the write when the serialized result drops sections that exist on disk, and log
    the refusal

Additional information

Nothing in ~/.codex keeps a backup of config.toml, so the old contents are gone once
the write lands. I recovered mine from VS Code's local history, which had kept 26 snapshots
because I'd opened the file in the editor months earlier. That is luck, not a recovery path
you can rely on.

For scale, the [projects.*] table alone held 29 entries, one per directory opened.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in codex_app_server::request_processors::config_processor and trace the config/batchWrite path during startup. Use a config.toml containing unmanaged sections and user-defined entries to investigate the short-lived app-server sequence. Done means batchWrite preserves unmanaged keys, or refuses and logs a destructive write, with a backup created if that protection is chosen.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.