can1357/oh-my-pi · error · ToolError
Shared browser attach failed: ${err instanceof Error ? err.m
Error message
Shared browser attach failed: ${err instanceof Error ? err.message : String(err)} What it means
The final catch-all of openSharedHeadlessHandle: any exception during shared-daemon setup that is not already a ToolAbortError/ToolError and is not explained by an aborted signal is wrapped as ToolError(`Shared browser attach failed: <message>`). It preserves the underlying message (from puppeteer.connect, broker RPC, etc.) while giving it a uniform tool-level shape.
Source
Thrown at packages/coding-agent/src/tools/browser/registry.ts:446
protocolTimeout: BROWSER_PROTOCOL_TIMEOUT_MS,
});
// Attaching to the shared daemon is the natural point to sweep targets
// left behind by omp processes that died without teardown — bounds
// accumulation without a background timer. Best-effort and detached so a
// slow reap never delays the open (issue #10022).
void reapOrphanSharedTargets(browser, { projectDir: shared.projectDir, daemonName: shared.daemonName });
return {
key: browserKey(kind),
kind,
browser,
sharedDaemon: { name: shared.daemonName, projectDir: shared.projectDir },
refCount: 0,
stealth: { browserSession: null, override: null },
};
} catch (err) {
if (err instanceof ToolAbortError || err instanceof ToolError) throw err;
if (opts.signal?.aborted) throw new ToolAbortError("Browser open aborted");
throw new ToolError(`Shared browser attach failed: ${err instanceof Error ? err.message : String(err)}`);
}
}
/** Test-only accessor for the module-global browsers map. */
export function getBrowsersMapForTest(): ReadonlyMap<string, BrowserHandle> {
return browsers;
}
View on GitHub (pinned to 9690622007)
Solutions
- Read the wrapped <message> in ~/.omp/logs and fix the root cause (usually daemon crash or stale endpoint)
- Stop stale daemons (`hub ps`, then stop/kill omp.browser.* daemons) and retry the open
- Retry the open — transient races between daemon start and attach usually resolve
- Use a dedicated spawned browser instead of the shared daemon if sharing keeps failing
Defensive patterns
Strategy: try-catch
Validate before calling
// confirm the shared endpoint is still alive before attaching
const alive = await fetch(`http://${daemonHost}/json/version`).then(r => r.ok).catch(() => false);
if (!alive) console.warn("Shared daemon endpoint dead; restart it before opening"); Try / catch
try {
return await openSharedHeadlessHandle(kind, opts);
} catch (err) {
if (err instanceof ToolError && err.message.startsWith("Shared browser attach failed:")) {
// log err.message for root cause; restart stale daemons and retry once
}
throw err;
} Prevention
- Stop stale omp.browser.* daemons (hub ps) before long test runs
- Watch ~/.omp/logs for daemon OOM/crash patterns
- Avoid concurrent opens racing daemon restarts; serialize first-open
- Fall back to a dedicated spawned browser when sharing is unstable
When it happens
Trigger: puppeteer.connect({browserWSEndpoint: shared.wsEndpoint}) throwing (daemon died between start and connect), broker RPC returning unexpected results, race where the daemon exits while refCount bookkeeping runs, or any unexpected exception in the attach path.
Common situations: Shared daemon crashed or was killed (OOM, hub stop) right after start; wsEndpoint stale after daemon restart; concurrent opens racing daemon teardown; transient network/socket errors to the daemon.
Related errors
- Browser websocket endpoint is unavailable
- No page targets available on the attached browser
- Connected to ${cdpUrl} but puppeteer.connect failed: ${(err
- Target ${payload.targetId} is no longer available on the att
- Target ${targetId} is no longer available on the attached br
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/eb1688f6483e1bea.
Report an issue: GitHub.