jackwener/OpenCLI · error · CommandExecutionError
${label} returned malformed JSON: ${err?.message ?? err}
Error message
${label} returned malformed JSON: ${err?.message ?? err} What it means
CommandExecutionError thrown by pypiFetch when resp.json() fails to parse the response body. The endpoint returned a 2xx response but the body is not valid JSON. The original parse error message is appended to help diagnose what was actually returned.
Source
Thrown at clis/pypi/utils.js:52
}
if (resp.status === 404) {
throw new EmptyResultError(label, `PyPI returned 404 for ${url}.`);
}
if (resp.status === 429) {
throw new CommandExecutionError(
`${label} returned HTTP 429 (rate limited)`,
'PyPI 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
- curl the same URL and inspect the raw body to see what is being returned
- Disable or bypass the interfering proxy/VPN
- Check that no HTTPS-intercepting appliance is rewriting the response
- Retry — truncation from flaky connections is often transient
Example fix
// diagnose before fixing curl -s https://pypi.org/pypi/requests/json | head -c 200 // if HTML appears, bypass proxy: # after (shell) NO_PROXY=pypi.org pypi package requests
Defensive patterns
Strategy: fallback
Validate before calling
const res = await fetch(url);
const text = await res.text();
try { JSON.parse(text); } catch { console.error('Non-JSON body (proxy/portal?):', text.slice(0, 120)); } Type guard
function isJsonObject(text) { try { const v = JSON.parse(text); return v !== null && typeof v === 'object'; } catch { return false; } } Try / catch
try {
const pkg = await pypiPackage(name);
} catch (e) {
if (/malformed JSON/i.test(e.message)) {
// inspect raw body / bypass proxy, then retry
} else throw e;
} Prevention
- Check for captive portals or HTTPS-intercepting proxies on the network
- curl the endpoint and inspect raw bytes when JSON errors appear
- Use trusted networks or NO_PROXY overrides for pypi.org
When it happens
Trigger: A 200 response whose body is HTML (login/interstitial page from a proxy or captive portal), a truncated response, or a mirror returning a non-JSON error page with a success status.
Common situations: Corporate proxies injecting HTML into responses; airport/hotel captive portals; content-filtering middleboxes; broken custom mirrors or registries.
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
- Chess.com API returned malformed JSON for ${url}: ${error?.m
- coingecko returned malformed JSON: ${error?.message || error
- ${label} returned malformed JSON: ${err?.message ?? err}
- Gmail ${operation} returned malformed JSON
- ${label} returned malformed JSON: ${err?.message ?? err}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/a0a1ed8557013145.
Report an issue: GitHub.