{"record":{"id":"a9246b2ae47303de","repo":"jackwener/OpenCLI","slug":"label-returned-malformed-json-err-message-a9246b","errorCode":null,"errorMessage":"${label} returned malformed JSON: ${err?.message ?? err}","messagePattern":"(.+?) returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/homebrew/utils.js","lineNumber":88,"sourceCode":"    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `Homebrew API returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'Homebrew throttles bursts; wait a few seconds and retry.',\n        );\n    }\n    if (!resp.ok) {\n        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);\n    }\n    let body;\n    try {\n        body = await resp.json();\n    }\n    catch (err) {\n        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);\n    }\n    return body;\n}\n\n/** Coerce a count value (which Homebrew analytics serves as `\"139,972\"`) to a plain number. */\nexport function parseInstallCount(value) {\n    if (value == null) return null;\n    const s = String(value).replace(/,/g, '').trim();\n    if (!s) return null;\n    const n = Number(s);\n    return Number.isFinite(n) ? n : null;\n}\n","sourceCodeStart":70,"sourceCodeEnd":101,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/homebrew/utils.js#L70-L101","documentation":"After a successful HTTP response, brewFetch calls resp.json(); if the body is not valid JSON (HTML error page, truncated response, proxy interception page) it throws this CommandExecutionError including the underlying JSON parse message. This guards against consuming a response that looked OK but isn't the expected API payload.","triggerScenarios":"A proxy/captive portal returning HTTP 200 with an HTML login page; a CDN/GitHub Pages error page served with 200; truncated response body on flaky connections; pointing brewFetch at a mirror that returns non-JSON content.","commonSituations":"Hotel/airport Wi-Fi captive portals hijacking requests; SSL-inspection appliances injecting pages; custom BREW_BASE mirrors misconfigured to serve HTML; network middleware in tests returning stub bodies.","solutions":["Inspect the raw response with curl to see what body actually comes back for that URL.","Bypass or fix the intercepting proxy/captive portal (authenticate, or switch networks).","If using a mirror, ensure it serves the exact JSON files; fix its content-type and body.","Retry once the network path returns real JSON; wrap in try-catch to surface a clear message."],"exampleFix":"// before\nconst body = await brewFetch(url, 'formula info'); // 200 + HTML -> malformed JSON error\n// after\ntry {\n  const body = await brewFetch(url, 'formula info');\n} catch (err) {\n  if (/malformed JSON/.test(err.message)) {\n    const raw = await fetch(url); console.log(await raw.text()); // inspect actual body\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  const body = await brewFetch(url, label);\n  return body;\n} catch (err) {\n  if (err instanceof CommandExecutionError && err.message.includes('malformed JSON')) {\n    // proxy/captive-portal or outage page — inspect raw body, switch network, or use cache\n    return getCachedOrEmpty(label);\n  }\n  throw err;\n}","preventionTips":["Complete captive-portal auth before running scripts on public Wi-Fi.","Exempt formulae.brew.sh from TLS inspection/proxy rewriting.","If mirroring the API, verify the mirror serves byte-identical JSON with application/json.","Spot-check with `curl -s <url> | head` that the body is JSON before bulk runs."],"tags":["json-parse","malformed-response","network"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}