jackwener/OpenCLI · error · CommandExecutionError
${label} returned malformed JSON: ${err?.message ?? err}
Error message
${label} returned malformed JSON: ${err?.message ?? err} What it means
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.
Source
Thrown at clis/homebrew/utils.js:88
}
if (resp.status === 404) {
throw new EmptyResultError(label, `Homebrew API returned 404 for ${url}.`);
}
if (resp.status === 429) {
throw new CommandExecutionError(
`${label} returned HTTP 429 (rate limited)`,
'Homebrew throttles bursts; wait a few seconds and retry.',
);
}
if (!resp.ok) {
throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
}
let body;
try {
body = await resp.json();
}
catch (err) {
throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
}
return body;
}
/** Coerce a count value (which Homebrew analytics serves as `"139,972"`) to a plain number. */
export function parseInstallCount(value) {
if (value == null) return null;
const s = String(value).replace(/,/g, '').trim();
if (!s) return null;
const n = Number(s);
return Number.isFinite(n) ? n : null;
}
View on GitHub (pinned to 49907e53dc)
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.
Example fix
// before
const body = await brewFetch(url, 'formula info'); // 200 + HTML -> malformed JSON error
// after
try {
const body = await brewFetch(url, 'formula info');
} catch (err) {
if (/malformed JSON/.test(err.message)) {
const raw = await fetch(url); console.log(await raw.text()); // inspect actual body
}
throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try {
const body = await brewFetch(url, label);
return body;
} catch (err) {
if (err instanceof CommandExecutionError && err.message.includes('malformed JSON')) {
// proxy/captive-portal or outage page — inspect raw body, switch network, or use cache
return getCachedOrEmpty(label);
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- coingecko categories returned malformed JSON: ${err?.message
- ${errorPrefix ? `${errorPrefix}: ` : ''}Boss API returned ma
- Boss API request failed: ${message}
- Chess.com API returned malformed JSON for ${url}: ${error?.m
- coingecko exchanges returned malformed JSON: ${err?.message
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/a9246b2ae47303de.
Report an issue: GitHub.