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
- 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.
- 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.
- 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
- Close the browser before disabling so it cannot rewrite its service file after restore
- Investigate and fix any earlier restore failure (external change / concurrent change) immediately instead of leaving the modified helper in place
- After a browser update, run reconcile so recovery material is re-validated against the new runtime files
- Do not restore old state_root snapshots that no longer match the installed browser version
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
- restore conflict: file already exists
- Backup source database is not an allowed local storage path
- Backup source database not found
- Codex CLI 路径指向的是目录而不是可执行文件:
- Codex CLI 没有执行权限:
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)