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, npmFetch parses the body as JSON. If resp.json() throws — meaning the endpoint returned HTML, an error page, empty content, or truncated data instead of valid JSON — the helper wraps the parse failure in a CommandExecutionError noting the label and underlying parse message. This indicates the response did not come from a healthy JSON API endpoint.
Source
Thrown at clis/npm/utils.js:73
}
if (resp.status === 404) {
throw new EmptyResultError(label, `npm registry returned 404 for ${url}.`);
}
if (resp.status === 429) {
throw new CommandExecutionError(
`${label} returned HTTP 429 (rate limited)`,
'npm throttles unauthenticated 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;
}
View on GitHub (pinned to 49907e53dc)
Solutions
- Fetch the same URL with `curl -i <url>` and inspect the raw body to see what is actually being returned (HTML page? empty? proxy notice?)
- Check whether a corporate proxy/captive portal is intercepting traffic and authenticate or bypass it
- Retry after a short delay — intermittent truncation from flaky networks resolves on retry
- Ensure requests go to the official endpoints (registry.npmjs.org, api.npmjs.org) and no overridden registry URL (npm config get registry) is mangling responses
Example fix
// before
const data = await npmFetch(url, 'npm package');
// after
try {
const data = await npmFetch(url, 'npm package');
} catch (err) {
if (String(err.message).includes('malformed JSON')) {
console.error('Registry returned non-JSON content; check proxy/network or retry.');
}
throw err;
} Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
try {
return await npmFetch(url, label);
} catch (err) {
if (/malformed JSON/.test(String(err.message))) {
// Likely proxy/interstitial or truncated response — retry once
await sleep(500);
return await npmFetch(url, label);
}
throw err;
} Prevention
- Verify network/proxy health: a captive portal or HTML-injecting proxy breaks JSON APIs
- Pin requests to official endpoints; do not point the tool at a registry mirror that returns HTML
- Retry transient truncations from flaky mobile/VPN connections
- Inspect raw responses with `curl -i` when malformed-JSON errors repeat for the same URL
When it happens
Trigger: The registry or an intermediary returns non-JSON content for a URL that npmFetch expected to be JSON: a captive-portal/HTML login page from a proxy, an npm incident serving an HTML error page with 200 status, a truncated gzip body from a flaky connection, or an empty body from a misbehaving cache/CDN edge.
Common situations: Corporate networks or VPNs injecting HTML interstitial pages; malformed responses from custom registry mirrors or cached proxies; DNS hijacking redirects; requests that hit api.npmjs.org rate-limit pages with unexpected content types.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Chess.com API returned malformed JSON for ${url}: ${error?.m
- ${label} returned malformed JSON: ${err?.message ?? err}
- 12306 ${endpoint} returned an unexpected payload shape
- archive search returned malformed JSON: ${error?.message ||
- archive snapshots returned malformed JSON: ${error?.message
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/25d86a80051d1558.
Report an issue: GitHub.