config/batchWrite on startup rewrites config.toml and drops all unmanaged keys
Nobody has claimed this yet.
- 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_urlkeys [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:
config/batchWriteran 10 times between Aug 22 and Aug 30 (Aug 22, 26, 28 x2, 29 x3,
30 x2) and preserved the file every time.- The next launch an hour later, at 15:04:20, issued no
config/batchWriteat all and
left the config intact. The write is not part of every startup. - The
apps_mcp_path_overridewarning is probably a red herring. It shows up on Aug 22,
25, 28 and 30 too, with no data loss. - No
WARNorERRORappears 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.tomltoconfig.toml.bakbefore 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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