can1357/oh-my-pi · error · ToolError

Browser websocket endpoint is unavailable

Error message

Browser websocket endpoint is unavailable

What it means

buildInitPayload reads the Puppeteer browser's WebSocket endpoint (browser.wsEndpoint()) to hand it to the tab worker. If the endpoint is absent the browser was not launched with a WS listener (or is already disconnected), so the worker cannot attach and a ToolError is thrown.

Source

Thrown at packages/coding-agent/src/tools/browser/tab-supervisor.ts:772

		if (await releaseTab(name, opts)) count++;
	}
	return count;
}

/** Test-only accessor for the module-global tabs map. */
export function getTabsMapForTest(): ReadonlyMap<string, TabSession> {
	return tabs;
}

function isLastSurfaceCloseError(err: unknown): boolean {
	const message = err instanceof Error ? err.message : String(err);
	return /last/i.test(message);
}

async function buildInitPayload(browser: PuppeteerBrowserHandle, opts: AcquireTabOptions): Promise<WorkerInitPayload> {
	const safeDir = getPuppeteerDir();
	const browserWSEndpoint = browser.browser.wsEndpoint();
	if (!browserWSEndpoint) throw new ToolError("Browser websocket endpoint is unavailable");
	if (browser.kind.kind === "headless") {
		return {
			mode: "headless",
			browserWSEndpoint,
			safeDir,
			// Visible launches still need an OMP-owned page, stealth setup, and
			// independent lifecycle; only their fixed device emulation is disabled.
			emulateViewport: browser.kind.headless,
			viewport: opts.viewport,
			dialogs: opts.dialogs,
			url: opts.url,
			waitUntil: opts.waitUntil,
			timeoutMs: opts.timeoutMs,
		};
	}
	// Connected and relay browsers are user-driven. When no target is requested,
	// adopt the visible tab and avoid raising it before screenshots. An explicit
	// target may be backgrounded, so retain activation for target-correct pixels.

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the browser process is running and was launched with a WS endpoint; close and relaunch the browser.
  2. Re-open the tab — a fresh acquire will relaunch a healthy browser if the old one died.
  3. Check system resources/logs for Chrome startup crashes (OOM, missing sandbox flags).
  4. Ensure nothing in the environment forces pipe transport or disables the DevTools WS listener.

Example fix

// before
const stale = getBrowserHandle(); // process died
await openTab(stale); // wsEndpoint() empty
// after
await closeBrowser(stale, { kill: true });
const tab = await openTab({ name: "docs", url }); // relaunches browser with WS endpoint
Defensive patterns

Strategy: try-catch

Validate before calling

const ep = handle.browser.wsEndpoint();
if (!ep || !handle.browser.connected) {
  await relaunchBrowser();
}

Type guard

function hasWsEndpoint(b: { wsEndpoint(): string | undefined; connected: boolean }): boolean {
  return b.connected && typeof b.wsEndpoint() === "string" && b.wsEndpoint().length > 0;
}

Try / catch

try {
  await openTab(handle);
} catch (e) {
  if (e instanceof ToolError && e.message === "Browser websocket endpoint is unavailable") {
    await closeBrowser(handle, { kill: true });
    return openTab(freshHandle()); // relaunch
  }
  throw e;
}

Prevention

When it happens

Trigger: Browser handle launched without a websocket endpoint (e.g. launched with pipe transport), the browser process exited/died before the worker attached, or the handle references a disconnected browser instance.

Common situations: Browser crashed between launch and worker spawn; browser launched with browserWSEndpoint disabled / pipe mode; reusing a stale handle after the process was killed; resource limits (OOM) killing Chrome at startup.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/9f8f12165b655b16. Report an issue: GitHub.