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
- Confirm the browser process is running and the port matches --remote-debugging-port
- Probe the URL manually (curl http://127.0.0.1:<port>/json/version) to see the actual response
- Check the browser's stderr/stdout for crash or port-binding errors
- Ensure proxy env vars exclude localhost (NO_PROXY=127.0.0.1,localhost)
- 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
- Pick a free port and pass it explicitly to --remote-debugging-port
- Set NO_PROXY=127.0.0.1,localhost so probes are not proxied
- Check browser stderr for startup crashes before attaching
- Wait/poll /json/version yourself before calling attach
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- tab.waitForResponse() timed out after ${timeoutMs}ms
- autoStarted ? `omp browser relay is serving at ${cdpUrl} but
- Failed to attach to ${path.basename(exe)} on ${cdpUrl}: ${(e
- Google browser search timed out.
- timed out: {command}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a289b5863feec021.
Report an issue: GitHub.