← Commit history

v2.0.686 (insiders): No more 'Something went wrong when opening your profile': after pup ends a browser it waits until no process still runs that profile folder before starting another (the old wait watched a lock file Chrome only creates on Linux/macOS, so on Windows the next browser started 1 s after a kill), and pup_restart_browser now ends the extension-lane browser itself instead of ending nothing and leaving the next open to force-kill it (John, AdomLapper, 2026-10-03).

John Lauer ·5d31492bd5 ·19d ago ·parent 9b76181
4 files changed +51−20
rust/Cargo.lock+12−12
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-pup-bridge"-version = "2.0.685"+version = "2.0.686" dependencies = [  "pup-browser",  "pup-cdp",@@ -655,7 +655,7 @@ dependencies = [  [[package]] name = "pup-browser"-version = "2.0.685"+version = "2.0.686" dependencies = [  "pup-core",  "pup-host",@@ -667,7 +667,7 @@ dependencies = [  [[package]] name = "pup-cdp"-version = "2.0.685"+version = "2.0.686" dependencies = [  "serde_json",  "thiserror",@@ -677,7 +677,7 @@ dependencies = [  [[package]] name = "pup-core"-version = "2.0.685"+version = "2.0.686" dependencies = [  "serde",  "serde_json",@@ -687,7 +687,7 @@ dependencies = [  [[package]] name = "pup-dashboard"-version = "2.0.685"+version = "2.0.686" dependencies = [  "pup-cdp",  "pup-core",@@ -700,7 +700,7 @@ dependencies = [  [[package]] name = "pup-host"-version = "2.0.685"+version = "2.0.686" dependencies = [  "serde",  "serde_json",@@ -709,14 +709,14 @@ dependencies = [  [[package]] name = "pup-host-mac"-version = "2.0.685"+version = "2.0.686" dependencies = [  "pup-host", ]  [[package]] name = "pup-host-win"-version = "2.0.685"+version = "2.0.686" dependencies = [  "pup-core",  "pup-host",@@ -726,7 +726,7 @@ dependencies = [  [[package]] name = "pup-identity"-version = "2.0.685"+version = "2.0.686" dependencies = [  "fontdue",  "image",@@ -736,7 +736,7 @@ dependencies = [  [[package]] name = "pup-session"-version = "2.0.685"+version = "2.0.686" dependencies = [  "pup-core",  "serde",@@ -746,7 +746,7 @@ dependencies = [  [[package]] name = "pup-vault"-version = "2.0.685"+version = "2.0.686" dependencies = [  "pup-core",  "pup-host",@@ -757,7 +757,7 @@ dependencies = [  [[package]] name = "pup-verbs"-version = "2.0.685"+version = "2.0.686" dependencies = [  "base64",  "png 0.17.16",
rust/Cargo.toml+1−1
@@ -4,7 +4,7 @@ members = ["core", "host", "host-win", "host-mac", "cdp", "browser", "session",  # One version for the whole bridge, in lockstep with src/BRIDGE_VERSION (tools/ship.sh bumps both). [workspace.package]-version = "2.0.685"+version = "2.0.686" edition = "2021" license = "MIT" 
rust/verbs/src/extras.rs+12
@@ -314,6 +314,18 @@ pub fn restart_browser(ctx: &Ctx, caller: &Caller, args: &Value) -> Value {     let waited = t0.elapsed().as_millis() as u64;     let _ = std::fs::remove_file(&port_file);     pup_core::log(format!("[restart] profile \"{profile}\" released after {waited} ms (pid alive: {})", pid > 0 && ctx.host.alive(pid)));+    // 2.0.686: the EXTENSION lane has no CDP browser handle (pid 0 above), so this verb ended nothing on it, and the+    // next open then force-killed the still-running browser as "stale" and started another one second later: John+    // got Chrome's "Something went wrong when opening your profile" (AdomLapper, 2026-10-03, asked by buck-repro).+    // End the lane browser here, after reserving its windows, and wait until no process holds the profile.+    if sessions.iter().any(|s| crate::transport::is_ext(s)) {+        let ids: Vec<String> = sessions.iter().map(|s| s.id.clone()).collect();+        crate::recovery::reserve(&ids); crate::recovery::mark_lost(&ids);+        crate::extdrive::enter_profile(&profile);+        let n = crate::kill_extdrive(ctx);+        crate::extdrive::leave_profile();+        pup_core::log(format!("[restart] profile \"{profile}\": ended its extension-lane browser ({n} process(es)) and waited for the profile to be free"));+    }     // 2.0.465: bring the windows back HERE, synchronously. Removing the browser handle above also removed the     // only thing the health loop watches, so the sessions sat in memory pointing at a dead browser and nothing     // relaunched them: pup_list_windows still listed them with their tabs, and every verb on them answered
rust/verbs/src/lib.rs+26−7
@@ -95,10 +95,35 @@ pub(crate) fn persist_lane(ctx: &Ctx) {     let _ = std::fs::write(&p, json!({ "lane": extdrive::st().token.lock().unwrap().clone(), "pids": extdrive::st().pids.lock().unwrap().clone() }).to_string()); } pub fn extdrive_browser_alive(ctx: &Ctx) -> bool { extdrive::st().pids.lock().unwrap().iter().any(|p| ctx.host.alive(*p)) }+/// 2.0.686 (John, AdomLapper, 2026-10-03: a "Something went wrong when opening your profile" dialog). After pup ends a+/// browser, wait until NO process still runs that profile folder before anything starts on it again. The old waits+/// looked for "SingletonLock", which Chrome only creates on Linux/macOS, so on Windows they returned at once and the+/// next browser started 1 s after a kill while the old one's helper processes still held the profile's files.+/// Stragglers are force-ended after 8 s; gives up (and says so) after `max`.+pub fn wait_profile_free(ctx: &Ctx, profile: &str, max: Duration) -> bool {+    let dir = pup_browser::profiles_dir(&*ctx.host).join(profile);+    let t0 = std::time::Instant::now(); let mut forced = false;+    loop {+        let left = orphan_browser_pids(ctx, &dir);+        if left.is_empty() {+            if t0.elapsed() > Duration::from_millis(300) { pup_core::log(format!("[extdrive] profile \"{profile}\" is free after {} ms", t0.elapsed().as_millis())); }+            return true;+        }+        if !forced && t0.elapsed() > Duration::from_secs(8) {+            for p in &left { ctx.host.kill(*p); }+            forced = true;+            pup_core::log(format!("[extdrive] profile \"{profile}\": {} process(es) still held it 8 s after the browser ended; force-ended them", left.len()));+        }+        if t0.elapsed() > max { pup_core::log(format!("[extdrive] profile \"{profile}\" still held by {} process(es) after {} s", left.len(), max.as_secs())); return false; }+        std::thread::sleep(Duration::from_millis(400));+    }+}+ pub fn kill_extdrive(ctx: &Ctx) -> u32 {     let pids: Vec<u32> = extdrive::st().pids.lock().unwrap().clone();     let mut n = 0;     for p in pids { if ctx.host.alive(p) { ctx.host.kill(p); n += 1; } }+    if n > 0 { let _ = wait_profile_free(ctx, &extdrive::cur_profile(), Duration::from_secs(20)); }     extdrive::st().pids.lock().unwrap().clear();     extdrive::st().token.lock().unwrap().clear();     extdrive::mark_dead();@@ -275,14 +300,8 @@ pub fn launch_extdrive(ctx: &Ctx, url: &str) -> Result<u32, Value> {             while t0.elapsed() < Duration::from_secs(10) { if !lock.exists() { break; } std::thread::sleep(Duration::from_millis(200)); }         }     }-    let stale = kill_extdrive(ctx);+    let stale = kill_extdrive(ctx);   // 2.0.686: kill_extdrive now waits until the profile is truly free     if stale > 0 {-        let lock = dir.join("SingletonLock");-        let t0 = std::time::Instant::now();-        while t0.elapsed() < Duration::from_secs(10) {-            if !lock.exists() { break; }-            std::thread::sleep(Duration::from_millis(200));-        }         pup_core::log(format!("[extdrive] ended {stale} stale browser process(es) holding profile \"{profile}\" before relaunching"));     }     // 2.0.646 (pup-bridge#121): an ORPHANED lane browser (launched by a pup instance that has since restarted,