From 919f1a7131ab37710ef6fc88eece87cfeecfbc91 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 14:58:47 -0300 Subject: [PATCH] fix(browse): kill old server before restart to prevent orphaned chromium processes When the health check fails or the server connection drops, `ensureServer()` and `sendCommand()` would call `startServer()` without first killing the previous server process. This left orphaned `chrome-headless-shell` renderer processes running at ~120% CPU each. After several reconnect cycles (e.g. pages that crash during hydration or trigger hard navigations via `window.location.href`), dozens of zombie chromium processes accumulate and exhaust system resources. Fix: call `killServer()` on the stale PID before spawning a new server in both the `ensureServer()` unhealthy path and the `sendCommand()` connection- lost retry path. Fixes #294 --- browse/src/cli.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/browse/src/cli.ts b/browse/src/cli.ts index 830b2e7c..07e29d5e 100644 --- a/browse/src/cli.ts +++ b/browse/src/cli.ts @@ -234,7 +234,10 @@ async function ensureServer(): Promise { } } - // Need to (re)start + // Need to (re)start — kill the old server first to avoid orphaned chromium processes + if (state && state.pid) { + await killServer(state.pid); + } console.error('[browse] Starting server...'); return startServer(); } @@ -289,6 +292,11 @@ async function sendCommand(state: ServerState, command: string, args: string[], if (err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET' || err.message?.includes('fetch failed')) { if (retries >= 1) throw new Error('[browse] Server crashed twice in a row — aborting'); console.error('[browse] Server connection lost. Restarting...'); + // Kill the old server to avoid orphaned chromium processes + const oldState = readState(); + if (oldState && oldState.pid) { + await killServer(oldState.pid); + } const newState = await startServer(); return sendCommand(newState, command, args, retries + 1); }