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, s2Fetch calls resp.json(); if the body is not valid JSON (truncated response, HTML error page, proxy interference), it throws this CommandExecutionError wrapping the underlying parse error message. It exists because Semantic Scholar sometimes serves non-JSON content on edge errors despite a 2xx/ok status.
Source
Thrown at clis/semanticscholar/utils.js:128
}
if (resp.status === 404) {
throw new EmptyResultError(label, `Semantic Scholar returned 404 for ${url}.`);
}
if (resp.status === 429) {
throw new CommandExecutionError(
`${label} returned HTTP 429 (rate limited)`,
'Semantic Scholar throttles anonymous traffic; set SEMANTIC_SCHOLAR_API_KEY (free at https://www.semanticscholar.org/product/api) or wait a minute 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}`);
}
if (body && typeof body === 'object' && body.error) {
throw new CommandExecutionError(`${label} returned an error: ${body.error}`);
}
return body;
}
/** Return the AI-generated one-line summary if present, else ''. */
export function tldrText(tldr) {
if (tldr && typeof tldr === 'object' && typeof tldr.text === 'string') {
return tldr.text.trim();
}
return '';
}
/** First author display name, or '' when authors is missing. */
export function firstAuthorName(authors) {
if (!Array.isArray(authors) || !authors.length) return '';View on GitHub (pinned to 49907e53dc)
Solutions
- Retry the request — this is often transient.
- Inspect the raw response text (curl the same URL with the same headers) to see what non-JSON content is returned.
- Bypass proxies/VPNs that may rewrite the response; check for captive portals.
- Verify the URL/fields are valid so the real API (not an error page) answers.
- If persistent, report to Semantic Scholar or add a fallback that treats it as an outage.
Example fix
// before: only JSON assumed
const body = await s2Fetch(url, 'paper');
// after: pre-check content type and degrade gracefully
const resp = await fetch(url, { headers: { accept: 'application/json' } });
const text = await resp.text();
let body;
try { body = JSON.parse(text); }
catch { throw new Error(`Unexpected non-JSON response: ${text.slice(0, 120)}`); } Defensive patterns
Strategy: try-catch
Type guard
function isJsonObject(v) {
return v !== null && typeof v === 'object' && !Array.isArray(v);
} Try / catch
try {
const body = await s2Fetch(url, 'paper');
} catch (err) {
if (/malformed JSON/.test(err.message)) {
// transient/proxy issue: retry once, then fail with context
return retryWithBackoff(() => s2Fetch(url, 'paper'), 2);
}
throw err;
} Prevention
- Retry transient parse failures — they are usually momentary.
- Bypass proxies/VPNs that may inject HTML into responses.
- Send Accept: application/json (the adapter already does).
- Check for captive portals when on restricted networks.
When it happens
Trigger: resp.ok is true but resp.json() rejects: the API or an intermediary returned HTML (login/captcha page), an empty body, or a truncated payload for a graph/v1 or recommendations/v1 endpoint.
Common situations: Corporate proxies or captive portals injecting HTML into 200 responses, CDN edge errors returning empty bodies, network interruption mid-download, or hitting a mirror that returns plain text.
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 callback returned malformed JSON for ${url}: ${err
- mdn search returned malformed JSON: ${err?.message ?? err}
- Malformed JSON from Stack Exchange API for ${label}: ${detai
- ${label} returned malformed JSON: ${err?.message ?? err}
- wikipedia returned malformed JSON: ${error?.message || error
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/dcbe03478fd28924.
Report an issue: GitHub.