{"record":{"id":"efc92fff6af972f8","repo":"microsoft/playwright","slug":"ios-webkit-debug-proxy-proxybase-json-returned","errorCode":null,"errorMessage":"ios_webkit_debug_proxy ${proxyBase}/json returned ${res.status}","messagePattern":"ios_webkit_debug_proxy (.+?)/json returned (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/webkit/webview/wvBrowser.ts","lineNumber":69,"sourceCode":"};\n\nfunction deriveProxyBase(endpointURL: string): string {\n  const u = new URL(endpointURL);\n  if (u.protocol === 'http:' || u.protocol === 'https:')\n    return `${u.protocol}//${u.host}`;\n  const httpProto = u.protocol === 'wss:' ? 'https:' : 'http:';\n  return `${httpProto}//${u.host}`;\n}\n\nfunction pageIdFromWsUrl(wsUrl: string): string {\n  const m = /\\/devtools\\/page\\/([^/]+)/.exec(wsUrl);\n  return m ? m[1] : wsUrl;\n}\n\nasync function listTabs(proxyBase: string, headers: { [key: string]: string }): Promise<ProxyTab[]> {\n  const res = await fetch(`${proxyBase}/json`, { headers });\n  if (!res.ok)\n    throw new Error(`ios_webkit_debug_proxy ${proxyBase}/json returned ${res.status}`);\n  const data = await res.json() as ProxyTab[];\n  return data.filter(t => !!t.webSocketDebuggerUrl);\n}\n\n// Local WebSocket-backed transport: defers opening the socket until `open()`\n// is called, so listeners on `onmessage` are wired before the remote side\n// starts emitting events.\nclass DeferredWebSocketTransport implements ConnectOverCDPTransport {\n  private readonly _url: string;\n  private readonly _headers: { [key: string]: string };\n  private _ws: ws | undefined;\n  private _closed = false;\n\n  onmessage?: (message: object) => void;\n  onclose?: (reason?: string) => void;\n\n  constructor(url: string, headers: { [key: string]: string }) {\n    this._url = url;","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/webkit/webview/wvBrowser.ts#L51-L87","documentation":"When connecting to Mobile Safari via ios_webkit_debug_proxy, Playwright fetches `${proxyBase}/json` to list debuggable tabs. If that HTTP endpoint returns a non-OK status (any non-2xx), listing aborts. The proxy must be reachable and healthy, and it must be the ios_webkit_debug_proxy endpoint (or a compatible CDP-over-HTTP gateway), not an arbitrary URL.","triggerScenarios":"Calling connectOverCDP with a webview/Safari endpoint whose /json returns an error (404, 500, 502); pointing at a wrong port (another dev server); the proxy crashed or isn't ios_webkit_debug_proxy; auth required and not supplied via headers.","commonSituations":"Wrong port for ios_webkit_debug_proxy; proxy not started; device disconnected so proxy returns an error; pointing at a Chrome DevTools /json endpoint that behaves differently.","solutions":["Verify the proxy: `curl http://<host>:<port>/json` should return a JSON tab array.","Start/restart ios_webkit_debug_proxy and confirm the device is trusted and connected.","Pass the correct base URL (scheme + host + port) — Playwright derives proxyBase from your endpoint."],"exampleFix":"// before\nconst browser = await webkit.connectOverCDP('http://localhost:9222'); // wrong port\n\n// after\n// first: curl http://localhost:27753/json  (ios_webkit_debug_proxy default)\nconst browser = await webkit.connectOverCDP('http://localhost:27753');","handlingStrategy":"validation","validationCode":"async function checkProxy(base) {\n  const res = await fetch(`${base}/json`);\n  if (!res.ok) throw new Error(`ios_webkit_debug_proxy unhealthy (${res.status}) at ${base}`);\n  return res.json();\n}\nawait checkProxy(endpointBase);","typeGuard":null,"tryCatchPattern":"try { browser = await webkit.connectOverCDP(base); }\ncatch (e) {\n  if (/\\/json returned \\d+/.test(String(e.message))) {\n    throw new Error(`Proxy unhealthy — restart ios_webkit_debug_proxy at ${base}`);\n  }\n  throw e;\n}","preventionTips":["Start ios_webkit_debug_proxy before connecting and confirm `curl <base>/json` returns JSON.","Point at the correct port (commonly 27753).","Keep the device trusted and unlocked so the proxy stays healthy."],"tags":["webview","ios","safari","proxy","network"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}