can1357/oh-my-pi · error · ToolError

Timed out waiting for CDP endpoint ${cdpUrl}${lastStatus !==

Error message

Timed out waiting for CDP endpoint ${cdpUrl}${lastStatus !== null ? `: HTTP ${lastStatus}` : ""}

What it means

waitForCdp polls a Chromium DevTools Protocol HTTP endpoint (e.g. http://127.0.0.1:9222/json/version) until it returns a 2xx status. If the endpoint never becomes healthy before the wait budget expires, it throws ToolError with the CDP URL and the last HTTP status observed (if any probe actually answered). This guards against attaching to a browser that never exposed its debugging port.

Source

Thrown at packages/coding-agent/src/tools/browser/attach.ts:117

		finish(null);
	}
	return promise;
}

/** Poll `${cdpUrl}/json/version` until it responds with 200, with abort + timeout support. */
export async function waitForCdp(cdpUrl: string, timeoutMs: number, signal?: AbortSignal): Promise<void> {
	const deadline = Date.now() + timeoutMs;
	const probeUrl = `${cdpUrl.replace(/\/+$/, "")}/json/version`;
	let lastStatus: number | null = null;
	while (Date.now() < deadline) {
		throwIfAborted(signal);
		const status = await probeCdpStatus(probeUrl, { timeoutMs: 2000, signal });
		if (status !== null && status >= 200 && status < 300) return;
		lastStatus = status;
		await Bun.sleep(150);
	}
	throwIfAborted(signal);
	throw new ToolError(
		`Timed out waiting for CDP endpoint ${cdpUrl}${lastStatus !== null ? `: HTTP ${lastStatus}` : ""}`,
	);
}

/**
 * Pull a `--remote-debugging-port=<n>` value out of an argv array (Chromium
 * accepts both `--flag=value` and `--flag value`). Returns null if absent or
 * malformed.
 */
function findCdpPortInArgs(args: string[]): number | null {
	for (const arg of args) {
		const m = /^--remote-debugging-port=(\d+)$/.exec(arg);
		if (m) {
			const port = Number.parseInt(m[1]!, 10);
			if (Number.isFinite(port) && port > 0) return port;
		}
	}
	for (let i = 0; i < args.length - 1; i++) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Confirm the browser process is running and the port matches --remote-debugging-port
  2. Probe the URL manually (curl http://127.0.0.1:<port>/json/version) to see the actual response
  3. Check the browser's stderr/stdout for crash or port-binding errors
  4. Ensure proxy env vars exclude localhost (NO_PROXY=127.0.0.1,localhost)
  5. Increase the wait budget or retry the attach if the machine is slow to start the browser

Example fix

// before
await openBrowserHandle({ cdpUrl: "http://127.0.0.1:9222" }); // browser slow to bind
// after
// 1. launch with an explicit free port
// chrome --remote-debugging-port=9222
// 2. verify before attaching:
const ok = await fetch("http://127.0.0.1:9222/json/version").then(r => r.ok).catch(() => false);
if (!ok) throw new Error("CDP endpoint not ready");
await openBrowserHandle({ cdpUrl: "http://127.0.0.1:9222" });
Defensive patterns

Strategy: retry

Validate before calling

async function cdpReady(url: string, timeoutMs = 10000): Promise<boolean> {
  const end = Date.now() + timeoutMs;
  while (Date.now() < end) {
    const ok = await fetch(`${url}/json/version`).then(r => r.ok).catch(() => false);
    if (ok) return true;
    await Bun.sleep(250);
  }
  return false;
}
if (!(await cdpReady(cdpUrl))) throw new Error("CDP endpoint never became healthy");

Try / catch

try {
  await waitForCdp(cdpUrl, { signal });
} catch (e) {
  if (e instanceof ToolError && e.message.includes("Timed out waiting for CDP endpoint")) {
    // inspect lastStatus in message; restart browser with a fresh port and retry
  } else throw e;
}

Prevention

When it happens

Trigger: Attaching to a Chromium instance whose --remote-debugging-port never opened; browser process crashed during startup; wrong port/host in the CDP URL; devtools server bound to a different interface (e.g. only IPv6 or a non-localhost address); proxy env vars intercepting localhost requests.

Common situations: Launching Electron/Chrome with a bad or already-used debugging port; container networking hiding the port from the host; slow machine where the browser takes longer than the wait window; NO_PROXY not set so HTTP_PROXY breaks the 127.0.0.1 probe (HTTP 4xx/5xx in the message).

Understand the failure class

Related errors


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