{"record":{"id":"a595f5ddceafbdb2","repo":"puppeteer/puppeteer","slug":"could-not-parse-json-from-url","errorCode":null,"errorMessage":"Could not parse JSON from ${url}","messagePattern":"Could not parse JSON from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/browsers/src/httpUtil.ts","lineNumber":171,"sourceCode":"          }\n        });\n        response.pipe(file);\n      });\n      request.on('error', error => {\n        return reject(error);\n      });\n    } catch (error) {\n      reject(error);\n    }\n  });\n}\n\nexport async function getJSON(url: URL): Promise<unknown> {\n  const text = await getText(url);\n  try {\n    return JSON.parse(text);\n  } catch {\n    throw new Error('Could not parse JSON from ' + url.toString());\n  }\n}\n\nexport function getText(url: URL): Promise<string> {\n  return new Promise(async (resolve, reject) => {\n    try {\n      const request = await httpRequest(\n        url,\n        'GET',\n        response => {\n          let data = '';\n          if (response.statusCode && response.statusCode >= 400) {\n            return reject(new Error(`Got status code ${response.statusCode}`));\n          }\n          response.on('data', chunk => {\n            data += chunk;\n          });\n          response.on('end', () => {","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/puppeteer/puppeteer/blob/d484e21c17f6023826fefdcdeeb553e04a7aaed8/packages/browsers/src/httpUtil.ts#L153-L189","documentation":"Thrown by getJSON when the body fetched from url could not be JSON.parse'd. The original SyntaxError is swallowed and replaced with a clearer message naming the URL. Because getText already handles HTTP-level errors (>=400), reaching JSON.parse means the request returned 2xx with a non-JSON body — typically an HTML error/landing page from a proxy or a misrouted URL.","triggerScenarios":"A custom baseUrl returns HTML (login page, 200-status error page) instead of JSON; a corporate proxy intercepts with a captive portal; product-details / JSON endpoints moved and now 302 to an HTML page; trailing slash differences yielding a directory listing.","commonSituations":"Misconfigured baseUrl for Firefox product-details; storage bucket returning XML error JSON; localized ISP injection; the JSON endpoint changed path upstream.","solutions":["Manually curl the URL and inspect Content-Type and the first bytes of the body.","Correct baseUrl to the official endpoint or a mirror known to serve JSON.","Bypass/whitelist the endpoint in your proxy to prevent HTML injection.","Catch the error and fall back to a known-good pinned URL."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"async function bodyIsJson(url: URL): Promise<boolean> {\n  const res = await fetch(url);\n  const ct = res.headers.get('content-type') ?? '';\n  return ct.includes('application/json') && res.ok;\n}\nif (!await bodyIsJson(url)) throw new Error(`Endpoint ${url} does not serve JSON`);","typeGuard":null,"tryCatchPattern":"try {\n  return await getJSON(url);\n} catch (e) {\n  if ((e as Error).message.startsWith('Could not parse JSON')) {\n    throw new Error(`${url} returned non-JSON (likely an HTML proxy/login page). Check baseUrl.`);\n  }\n  throw e;\n}","preventionTips":["Confirm baseUrl serves application/json with a quick curl before installing.","Whitelist browser endpoints in corporate proxies to avoid HTML injection.","Keep baseUrl at the official endpoints unless you fully control the mirror."],"tags":["network","json","download","proxy"],"backgroundTag":null,"analyzedSha":"d484e21c17f6023826fefdcdeeb553e04a7aaed8","analyzedAt":"2026-08-12T06:33:19.665Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}