BigPizzaV3/CodexPlusPlus · error
Recovery verification failed
Error message
Recovery verification failed
What it means
restore_all performs a prefetched, two-phase restore of the native browser service file in the runtime cache: it first collects all pending restorations, then for each one re-reads the file, atomically writes back the original content with the preserved mtime, and re-reads to verify. This ensure! fires when, immediately after the atomic write reported success, the file on disk does not equal the original bytes — i.e. the write-back did not durably land as expected. The library throws it as a fail-closed safety check because silently accepting an unrestored service file would leave a modified browser helper in place.
Solutions
- Close the browser (kill all its processes) so nothing rewrites the service file, then re-run reconcile(paths, false).
- Exclude the browser runtime cache and the state_root backup directory from antivirus/backup/sync software, then retry.
- Ensure only one launcher instance is running (the owner.lock holder); stop other instances and retry the restore.
- Check free disk space and filesystem health (chkdsk); if the cache is corrupted, let the desktop app delete the affected browser cache so restore skips the obsolete runtime, then reinstall/repair the browser.
Example fix
// before: restoring while the browser is running
reconcile(&paths, false)?; // -> Recovery verification failed
// after: shut down the browser first, then reconcile with retry
assert!(wait_browser_closed(&paths));
for _ in 0..3 {
match reconcile(&paths, false) {
Ok(status) => { break; }
Err(e) if e.to_string().contains("Recovery verification failed") => continue,
Err(e) => return Err(e),
}
} Defensive patterns
Strategy: retry
Validate before calling
// Before restoring, ensure the browser is not holding/rewriting the file
fn service_stable(paths: &BrowserPaths, key: &str) -> std::io::Result<bool> {
let target = paths.runtime_root.join(key).join("SERVICE");
let a = std::fs::read(&target)?;
std::thread::sleep(std::time::Duration::from_millis(500));
Ok(std::fs::read(&target)? == a)
} Try / catch
match reconcile(&paths, false) {
Err(e) if e.to_string().contains("Recovery verification failed") => {
// browser/AV interference: close browser, add AV exclusion, retry with backoff
retry_with_backoff(3, || reconcile(&paths, false));
}
other => other?,
} Prevention
- Always close the browser before disabling/ restoring native browser compatibility
- Exclude the browser cache and state_root directories from antivirus, backup, and cloud-sync tools
- Run only one launcher instance so the owner lock is uncontended
- Keep sufficient free disk space and periodically check filesystem health
When it happens
Trigger: restore_all (via reconcile_locked/reconcile on disable) after atomic_write_with_modified succeeds but a subsequent read_regular of the same target returns content != original. Concrete causes: another process (the browser itself or an antivirus) rewrote the file between the write and the verify read; the atomic rename was undone or intercepted; filesystem/caching anomalies where the rename is not yet visible to the read; or disk corruption of the freshly written file.
Common situations: Disabling native-browser compatibility while the browser is running and its service keeps rewriting its own file; antivirus or sync tools (OneDrive/Dropbox) holding or reverting files in the browser cache; a second CodexPlusPlus process racing the restore despite the owner lock; full disk or quota preventing a complete 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
- 拒绝删除文件系统根目录:
- File grew beyond the size limit
- File is a reparse point
- Parent directory is a reparse point
- Reparse observation file
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/3e69d5796c6e2e10.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/native_browser.rs:530
ensure!(
current == original || current == candidate,
"External runtime change prevents recovery"
);
if current == candidate {
let modified = UNIX_EPOCH
.checked_add(Duration::new(journal.modified_secs, journal.modified_nanos))
.context("Invalid recovery timestamp")?;
pending.push((target, modified, original, current));
}
}
// Preflight every cache before restoring any, independent of directory enumeration order.
for (target, modified, original, current) in pending {
ensure!(
read_regular(&target, MAX_SERVICE)? == current,
"Concurrent recovery change"
);
atomic_write_with_modified(&target, &original, Some(modified))?;
ensure!(
read_regular(&target, MAX_SERVICE)? == original,
"Recovery verification failed"
);
}
Ok(())
}
/// No runtime operation occurs when this feature has never been enabled.
/// Call only from the owning launcher, never from settings save or status inspection.
pub fn reconcile(paths: &BrowserPaths, enabled: bool) -> Result<BrowserStatus> {
reconcile_contract(paths, enabled, &RuntimeContract::pinned())
}
fn reconcile_contract(
paths: &BrowserPaths,
enabled: bool,
contract: &RuntimeContract,
) -> Result<BrowserStatus> {View on GitHub (pinned to b1ed92e5e4)