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

  1. Close the browser (kill all its processes) so nothing rewrites the service file, then re-run reconcile(paths, false).
  2. Exclude the browser runtime cache and the state_root backup directory from antivirus/backup/sync software, then retry.
  3. Ensure only one launcher instance is running (the owner.lock holder); stop other instances and retry the restore.
  4. 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

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


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)