{"record":{"id":"fcf58d460e2adde0","repo":"can1357/oh-my-pi","slug":"browser-app-cdp-url-must-be-the-http-cdp-discovery","errorCode":null,"errorMessage":"browser app.cdp_url must be the HTTP CDP discovery endpoint (for example http://127.0.0.1:9222), not a ws:// browser websocket URL.","messagePattern":"browser app\\.cdp_url must be the HTTP CDP discovery endpoint \\(for example http://127\\.0\\.0\\.1:9222\\), not a ws:// browser websocket URL\\.","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/registry.ts","lineNumber":157,"sourceCode":"\t\t// orphaned Chromium/app process / puppeteer handle survives to process\n\t\t// exit. (Issue #3963.)\n\t\tif (opts.signal?.aborted) {\n\t\t\tawait disposeBrowserHandle(handle, { kill: kind.kind === \"spawned\" }).catch(err => {\n\t\t\t\tlogger.debug(\"Failed to dispose orphan browser after abort\", {\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t});\n\t\t\t});\n\t\t\tthrow new ToolAbortError(\"Browser open aborted\");\n\t\t}\n\t\tbrowsers.set(key, handle);\n\t\treturn handle;\n\t}\n}\n\nexport function normalizeConnectedCdpUrl(rawCdpUrl: string): string {\n\tconst cdpUrl = rawCdpUrl.replace(/\\/+$/, \"\");\n\tif (/^wss?:\\/\\//i.test(cdpUrl)) {\n\t\tthrow new ToolError(\n\t\t\t\"browser app.cdp_url must be the HTTP CDP discovery endpoint (for example http://127.0.0.1:9222), not a ws:// browser websocket URL.\",\n\t\t);\n\t}\n\treturn cdpUrl;\n}\n\nasync function openBrowserHandle(kind: BrowserKind, opts: AcquireBrowserOptions): Promise<BrowserHandle> {\n\tif (kind.kind === \"cmux\") {\n\t\tconst client = new CmuxSocketClient({ socketPath: kind.socketPath, password: kind.password });\n\t\tawait client.connect();\n\t\treturn {\n\t\t\tkey: browserKey(kind),\n\t\t\tkind,\n\t\t\tclient,\n\t\t\tsurface: kind.surface,\n\t\t\trefCount: 0,\n\t\t};\n\t}","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/registry.ts#L139-L175","documentation":"normalizeConnectedCdpUrl canonicalizes a user-supplied CDP URL from the browser app config and rejects ws:// or wss:// URLs. Chrome exposes two different endpoints: an HTTP discovery endpoint (e.g. http://127.0.0.1:9222) used with puppeteer's browserURL, and a raw browser websocket URL. This library requires the HTTP discovery endpoint and throws ToolError when given a websocket URL instead.","triggerScenarios":"Setting browser app config cdp_url to a ws://... value, typically copied from the 'webSocketDebuggerUrl' field of /json/version output, or from a remote-debugging guide that shows the websocket URL.","commonSituations":"Users pasting the DevTools websocket URL from chrome://version or /json/version; misconfigured CI browser services that export ws endpoints; mixing up puppeteer.connect({browserWSEndpoint}) semantics with this tool's browserURL semantics.","solutions":["Change app.cdp_url to the HTTP discovery endpoint, e.g. http://127.0.0.1:9222 (strip the /devtools/browser/... path entirely)","If the browser runs on another host/port, point to http://<host>:<port> where the /json/version endpoint responds","Restart the browser with --remote-debugging-port=<port> if no HTTP endpoint exists"],"exampleFix":"// before\n{ \"cdp_url\": \"ws://127.0.0.1:9222/devtools/browser/8f6a-...\" }\n// after\n{ \"cdp_url\": \"http://127.0.0.1:9222\" }","handlingStrategy":"validation","validationCode":"function assertHttpCdpUrl(raw: string): string {\n  const trimmed = raw.replace(/\\/+$/, \"\");\n  if (!/^https?:\\/\\//i.test(trimmed)) {\n    throw new Error(`cdp_url must start with http:// or https:// (got ${raw})`);\n  }\n  return trimmed;\n}","typeGuard":"function isHttpCdpUrl(raw: string): boolean {\n  return /^https?:\\/\\//i.test(raw.trim());\n}","tryCatchPattern":null,"preventionTips":["Store the HTTP discovery endpoint (http://host:port) in browser app config, never webSocketDebuggerUrl","Remember: ws:// URLs come from /json/version's webSocketDebuggerUrl — the tool wants the /json/version base URL instead","Verify with `curl http://127.0.0.1:9222/json/version` that the configured endpoint is the HTTP one","Document config examples using http:// to prevent copy-paste of ws:// from other guides"],"tags":["configuration","cdp","validation","browser"],"backgroundTag":"invalid-endpoint-url","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}