Desktop: config.toml zero-filled to NUL bytes after ungraceful shutdown — non-atomic write on Windows loses entire file content

Open 💬 3 comments Opened Jun 4, 2026 by tasmeer-landally

Summary

Codex Desktop on Windows writes ~/.codex/config.toml non-atomically and without FlushFileBuffers. If the OS loses power (or is force-rebooted) between the moment Codex's write() returns and the moment NTFS flushes the dirty page cache to disk, NTFS commits the file's new size to the directory metadata but never writes the data clusters. Per the file-system's zero-fill-on-read security policy, the file then reads as uniform 0x00 bytes from byte 0 to EOF, blowing away every user customization (plugins, MCP servers, sandbox mode, model settings, trusted projects).

On next launch Codex bails with:

failed to load configuration: C:\Users\<user>\.codex\config.toml:1:<size+1>: key with no value, expected `=`

because the TOML parser walks N NUL bytes as one runaway bare key and hits EOF without finding =.

Reproduction

  1. On Windows 10/11, launch Codex Desktop and let it write config.toml (any settings change, plugin install, or [projects.*] trust prompt will do).
  2. Within ~30s of the write — before Windows' lazy writeback flushes the page cache — cut power (or hold the power button until the machine shuts down hard).
  3. Boot back up, launch Codex Desktop.
  4. Expected: previous config loads, or Codex falls back to defaults cleanly.
  5. Actual: ~/.codex/config.toml is N bytes of 0x00. Codex refuses to start with the parse error above. The "Use backup sandbox" button doesn't fix it because that only swaps sandbox mode — the parser fails before sandbox setup even runs.

Forensic evidence from one occurrence

File: C:\Users\<user>\.codex\config.toml
Size: 4183 bytes
First 16 bytes: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Last 16 bytes:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
LF count: 0
CR count: 0
NUL count: 4183
Unique byte values across whole file: { 0x00 }

File mtime matched the user's last Codex action ~5 minutes before the power loss. Three sibling files in .codex/ (AGENTS.md, auth.json, chrome-native-hosts-v2.json) were untouched by the same crash window because they hadn't been re-written that session — they survived intact.

Root cause

This is the classic ext4 zero-after-crash pattern, just on NTFS. Codex's persistence path appears to be a straight open(path, O_TRUNC) → write() → close(), without:

  • writing to a temp file (config.toml.tmp),
  • FlushFileBuffers(handle) to force the dirty cache to disk,
  • MoveFileEx(tmp, dst, MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING) for atomic replace.

Without these, the durability guarantee Codex appears to depend on doesn't exist on Windows.

Suggested fix

Adopt write-temp-then-rename atomic semantics for config.toml (and any other crash-critical files in ~/.codex/, e.g. auth.json, .codex-global-state.json, chrome-native-hosts-v2.json):

// pseudocode
let tmp = path.with_extension("toml.tmp");
{
    let mut f = File::create(&tmp)?;
    f.write_all(contents.as_bytes())?;
    f.sync_all()?;   // FlushFileBuffers under the hood on Windows
}
// On Windows: MoveFileExW(tmp, path, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)
// On POSIX:   rename(tmp, path)
fs::rename(tmp, path)?;

This makes the worst possible crash leave either the old intact file or the new intact file on disk — never a zero-filled phantom.

Secondary observation (separate, possibly related)

When a user hits this and clicks "Use backup sandbox", Codex tries to set up the elevated admin sandbox, which on some machines fails with "Couldn't set up admin sandbox" (see #14808, #23249). That makes the "Use backup sandbox" recovery path effectively a dead end for affected users — they have to fix the config file manually before Codex will boot at all.

Workaround for currently-affected users

  1. Move the zeroed file aside: Move-Item ~/.codex/config.toml ~/.codex/config.toml.corrupt
  2. If you've ever shared a session that included a Get-Content config.toml agent call, your old config may be recoverable from the JSONL rollout file.
  3. Otherwise: start Codex with an empty config, set [windows] sandbox = "unelevated" if you hit the secondary sandbox bug, re-trust your projects on demand.

Environment

  • OS: Windows 11 Pro 10.0.26200
  • Codex Desktop: latest as of 2026-06-04
  • Reproduction trigger: power loss during a Codex session

View original on GitHub ↗

3 Comments

alexdresko · 2 months ago

My codex app just crashed and I lost ALL of my projects and conversations.

gNikro · 1 month ago

Additional occurrence on Windows after an abrupt restart / forced reboot, on a newer Codex Desktop build.

Environment:

OS: Windows
Codex Desktop / app build observed in config/runtime env: 26.623.141536
Codex local state root: %USERPROFILE%\.codex
Date observed: 2026-07-08

User-visible symptom:

Fix C:\Users\<user>\.codex\config.toml:1:6427: key with no value, expected `=`.
After saving the file, reopen the thread.

Logs contained repeated entries:

Invalid configuration; using defaults. C:\Users\<user>\.codex\config.toml:1:6427: key with no value, expected `=`

Forensic evidence:

%USERPROFILE%\.codex\config2.toml        size=6426 bytes, nonzero byte count=0
%USERPROFILE%\.codex\config.backup.toml  size=6426 bytes, nonzero byte count=0
first 256 bytes of config2.toml: all 00

So both the manually saved broken config and the adjacent backup TOML were fully zero-filled, not merely syntactically malformed.

Impact in this occurrence:

  • Codex started using defaults after the parse failure.
  • Local project list was temporarily empty: app API returned {"schemaVersion":1,"projects":[]}.
  • Local threads were still present and recoverable from state_5.sqlite / list_threads; several private local workspaces were visible there, so session data was not fully lost.
  • Global MCP config was lost. Before repair, codex mcp list returned No MCP servers configured yet.
  • The affected profile lost or had to rebuild multiple global MCP servers.
  • Project-local MCP config survived where it lived outside the corrupted global file.

Recovery performed locally:

  • Rebuilt %USERPROFILE%\.codex\config.toml from surviving active defaults, session history, project-local .codex/config.toml files, VS Code mcp.json, and old rollout evidence.
  • Re-added trusted [projects.*] entries using state_5.sqlite thread cwd values.
  • Re-added global MCP servers using recovered TOML fragments from prior rollout logs.
  • Verified the repaired file with Python tomllib.
  • Verified codex mcp list after repair:
multiple global MCP servers enabled again

This reinforces the original report: config writes need crash-safe atomic temp-file + flush + rename semantics on Windows, and Codex should preserve a last-known-good config or auto-recover from project/thread state instead of falling back to defaults and losing MCP/project settings after a single corrupted TOML write.

Related broader state-loss issue: #26990

cyanlink · 1 month ago

Additional occurrence on Windows, with a newer Desktop build and no known power loss or forced shutdown.

Environment / timing:

OS: Windows
Codex Desktop build when diagnosed: 26.721.41059
Previous-day build evidence: 26.721.30844
Date observed: 2026-07-25
In-app feedback ID: 019f9a01-3cbf-76b1-8c21-2bcb20338d24

The app appeared normal the previous day. After the update/relaunch, the initial user-visible symptom was that all projects disappeared from the sidebar.

Recovery behavior was unusual:

  1. I found an older conversation through Recent and interacted with/reopened it.
  2. After that, exactly one project reappeared in the sidebar, together with that project's conversation list.
  3. Those conversations still could not be opened.
  4. At the same time, Desktop displayed the “Finish Windows setup” prompt.

Forensic evidence then showed:

%USERPROFILE%\.codex\config.toml
size: 5982 bytes
nonzero byte count: 0
SHA-256: 22575C4D5173905DEDE95DBFED575EC6DC129E87B0E4095F2EB0E5098E298C0F

The app-server error was:

C:\Users\<user>\.codex\config.toml:1:5983: key with no value, expected '='

An adjacent backup of the config was also uniformly NUL-filled, so it had no recovery value.

The project/conversation data itself was not deleted:

  • state_5.sqlite still contained the local threads and their workspace cwd values.
  • .codex-global-state.json had collapsed to a one-project state.
  • The local project migration markers were already recorded as completed (2026-07-13-local-projects and 2026-07-14-repair-metadata-local-projects), so restarting alone did not rebuild the sidebar.

Local recovery required fully exiting Codex, replacing the invalid config, and reconstructing the project/global-state JSON from surviving pre-migration state plus state_5.sqlite. Both the primary global-state file and its adjacent backup had to be repaired before restart. After that, all 8 projects and their conversations returned and could be opened.

This looks like the same zero-filled config.toml failure described in this issue, combined with the broader project/sidebar state loss tracked in #26990. The important difference is that this occurrence was correlated with a Desktop update/relaunch rather than a known power-loss event. The “Finish Windows setup” prompt also appears to be a misleading downstream symptom: invalid config/default fallback prevented normal Desktop settings and Windows sandbox initialization while the durable thread data remained present.

No raw logs, full config, project names, prompts, tokens, or private paths are included here. I can provide specific redacted diagnostics if maintainers identify the fields needed.