BigPizzaV3/CodexPlusPlus · error
Runtime write verification failed
Error message
Runtime write verification failed
What it means
After `atomic_write(&target, &candidate)`, `prepare` reads the target back and verifies it now equals the candidate bytes. A mismatch means the atomic write did not take effect as observed — the filesystem lied, another writer immediately overwrote the file, or the read hit a stale/cached view. Codex++ treats this as a post-write invariant violation and fails rather than reporting success with an unpatched or corrupted runtime.
Solutions
- Rerun reconcile on a local filesystem; move `CODEX_HOME` off network mounts if applicable.
- Check for and stop other processes (codex desktop, sync/AV) that touch the plugin cache, then retry.
- Verify disk health/free space (`dmesg`, `df`) if failures repeat.
- Capture `read_regular(&target)` contents after the failure to confirm what actually landed before filing a bug.
Example fix
// before // CODEX_HOME on NFS mount export CODEX_HOME=/mnt/nfs/home/.codex // after export CODEX_HOME="$HOME/.codex" # local disk, coherent fs semantics
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure CODEX_HOME is on a local filesystem with coherent write semantics
let meta = std::fs::metadata(codex_home)?; // reject network mounts in your launcher
if is_network_mount(&meta) { bail!("CODEX_HOME must be on a local filesystem"); } Try / catch
match reconcile(&paths, true) {
Err(e) if e.to_string().contains("Runtime write verification failed") => {
// inspect what actually landed, check for external writers / fs issues
let landed = std::fs::read(runtime_root.join(&key).join("service.mjs"))?;
eprintln!("write not observed; {} bytes landed", landed.len());
// move CODEX_HOME to local disk or quiesce writers, then retry
}
other => other?,
} Prevention
- Keep CODEX_HOME on local disk, not NFS/SMB/VM shared folders
- Exclude plugin cache from endpoint-protection rewrite rules
- Check disk health/free space if failures repeat
- Only one writer at a time for the plugin cache
When it happens
Trigger: `reconcile(paths, true)` completes `atomic_write` and the follow-up `read_regular(&target) != candidate`. Causes: an external process overwriting the file right after the write, network/overlay filesystem (network home dirs, some container mounts) with incoherent visibility, or full-disk conditions corrupting the write.
Common situations: Codex home on NFS/SMB or inside a VM shared folder; aggressive endpoint-protection rewriting files; another reconcile/codex update racing immediately after the write.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- 纯 API 配置写入后未检测到完整 custom provider,请检查 config.toml 和供应商 API…
- Codex CLI 路径指向的是目录而不是可执行文件:
- Codex CLI 没有执行权限:
- Codex CLI 路径不存在: 请填 Codex CLI 可执行文件的完整路径。macOS 桌面版通常在…
- .codex-global-state.json changed while deleting thread
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/9bdf31f0d9868ef8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/native_browser.rs:454
modified_nanos: modified.subsec_nanos(),
};
// Durable original and journal precede any runtime write.
atomic_write(&journal_path, &serde_json::to_vec(&journal)?)?;
}
let (journal, original, candidate) = recovery_material(paths, key, contract)?;
if current == candidate {
return Ok(());
}
ensure!(
current == original && sha(¤t) == journal.original_sha,
"Runtime changed outside Codex++; refusing to overwrite"
);
ensure!(
read_regular(&target, MAX_SERVICE)? == current,
"Concurrent runtime change"
);
atomic_write(&target, &candidate)?;
ensure!(
read_regular(&target, MAX_SERVICE)? == candidate,
"Runtime write verification failed"
);
Ok(())
}
fn recovery_material(
paths: &BrowserPaths,
key: &str,
contract: &RuntimeContract,
) -> Result<(Journal, Vec<u8>, Vec<u8>)> {
ensure!(key_valid(key), "Invalid recovery key");
let dir = paths.state_root.join(key);
let journal: Journal = serde_json::from_slice(&read_regular(&dir.join("journal.json"), 4096)?)?;
let original = read_regular(&dir.join("original.mjs"), MAX_SERVICE)?;
ensure!(
journal.candidate_sha.len() == 64
&& journal.candidate_sha.bytes().all(|b| b.is_ascii_hexdigit()),View on GitHub (pinned to b1ed92e5e4)