BigPizzaV3/CodexPlusPlus · error

Native browser service has not been restored

Error message

Native browser service has not been restored

What it means

verify_restored_state also checks every browser key that has recovery material (journal.json): if the corresponding runtime service file still exists, it must match the original bytes recovered from the backup. This ensure! fires when the runtime service file is present but differs from the original — meaning the modified/injected service was never reverted. The library throws it so callers cannot assume a clean shutdown while a patched helper is still deployed.

Solutions

  1. Run reconcile(&paths, false) (or reconcile_contract with the pinned contract) to perform a full restore_all pass, closing the browser first so it cannot rewrite the service file.
  2. If recovery_material/journal no longer matches the browser version, update/repair the browser (or let the desktop app clear its cache) so the runtime file is regenerated from the pristine original, then re-verify.
  3. Check diagnostic logs for the earlier restore failure (e.g. concurrent change, external modification) that left this key unrestored, fix that root cause (AV exclusion, single owner, browser closed), and retry.

Example fix

// before
monitor.stop().await; // verify_restored_state -> "service has not been restored"

// after
close_browser(&paths);
match reconcile(&paths, false) {
    Ok(status) => monitor.stop().await,
    Err(e) => log::error!("restore failed: {e}"),
}
Defensive patterns

Strategy: validation

Validate before calling

// Verify each journaled runtime service matches its original before relying on shutdown
fn services_restored(paths: &BrowserPaths) -> anyhow::Result<bool> {
    for entry in std::fs::read_dir(&paths.state_root)? {
        let key = entry?.file_name().to_string_lossy().to_string();
        let target = paths.runtime_root.join(&key).join("SERVICE");
        if target.exists() {
            let (_, original, _) = recovery_material(paths, &key, &RuntimeContract::pinned())?;
            if std::fs::read(&target)? != original { return Ok(false); }
        }
    }
    Ok(true)
}

Try / catch

if let Err(e) = monitor.stop_and_verify().await {
    if e.to_string().contains("has not been restored") {
        close_browser(&paths);
        reconcile(&paths, false)?; // run the full restore pass, then re-verify
    }
}

Prevention

When it happens

Trigger: wait_for_monitor_shutdown_at -> verify_restored_state when, for some key with journal.json, runtime_root/<key>/SERVICE exists and its content != the original recovered via recovery_material. Causes: restore_all never ran or failed for that key (e.g. an earlier 'External runtime change prevents recovery' or 'Recovery verification failed' abort), the browser re-modified the file after restore, or the journal's original no longer matches what the desktop app expects (contract/browser update changed the file).

Common situations: A previous failed disable left the modified helper in place and a later shutdown verify trips; the browser auto-updated and rewrote its service file between restore and verification; backup journal restored from an old state_root no longer matching the installed browser version.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/0780576794be5fa9. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/native_browser.rs:712

        return Ok(());
    }
    let control = paths.state_root.join("control.json");
    if control.exists() {
        let value: Value = serde_json::from_slice(&read_regular(&control, 1024)?)?;
        ensure!(
            value["schema"] == 1 && value["requireIdentification"] == false,
            "Native browser compatibility is still enabled"
        );
    }
    for entry in fs::read_dir(&paths.state_root)? {
        let key = entry?.file_name().to_string_lossy().to_string();
        if !key_valid(&key) || !paths.state_root.join(&key).join("journal.json").exists() {
            continue;
        }
        let target = paths.runtime_root.join(&key).join(SERVICE);
        if target.exists() {
            let (_, original, _) = recovery_material(paths, &key, &RuntimeContract::pinned())?;
            ensure!(
                read_regular(&target, MAX_SERVICE)? == original,
                "Native browser service has not been restored"
            );
        }
    }
    Ok(())
}

fn acquire_monitor_owner(paths: &BrowserPaths) -> Result<File> {
    plain_path(&paths.state_root)?;
    ensure!(
        !paths.state_root.starts_with(&paths.runtime_root),
        "Backups must be outside the cache"
    );
    fs::create_dir_all(&paths.state_root)?;
    let path = paths.state_root.join("monitor.lock");
    let _guards = pin_parents(&path)?;
    let mut options = OpenOptions::new();

View on GitHub (pinned to b1ed92e5e4)