jackwener/OpenCLI · error · Error
JSON parse failed (status=${response.status}, body[0..50]=${
Error message
JSON parse failed (status=${response.status}, body[0..50]=${JSON.stringify(trimmed.slice(0, 50))}): ${(err instanceof Error ? err.message : String(err))} What it means
When the response body genuinely is not HTML but JSON.parse still fails, parseJsonOrThrowLoginWall throws a plain Error embedding the HTTP status, the first 50 chars of the body (JSON-stringified), and the underlying parser message. This gives you the actual malformed payload inline so you don't need to reproduce the request to debug it.
Source
Thrown at src/utils.ts:162
contentType.toLowerCase().includes('text/html')
|| /^<(?:!doctype|html|head|body|title)(?:[\s>/]|$)/i.test(trimmed);
if (looksLikeHtml) {
throw new LoginWallError(
`Server returned HTML instead of JSON (status=${response.status}). `
+ `Likely a login wall, rate limit, or WAF challenge.`,
response.status,
opts.url || response.url || '',
trimmed.slice(0, 100),
);
}
try {
return JSON.parse(text);
} catch (err) {
// Real malformed JSON \u2014 surface body preview alongside the parser message
// so we don't have to repro to know what came back.
throw new Error(
`JSON parse failed (status=${response.status}, body[0..50]=${JSON.stringify(trimmed.slice(0, 50))}): `
+ (err instanceof Error ? err.message : String(err)),
);
}
}
/** Browser-side JS source fragment (as a string) that performs a `fetch` and
* either returns the parsed JSON body or a `LoginWallSignal` sentinel when
* the response is HTML. Intended to be embedded inside an adapter's
* `page.evaluate` block.
*
* Usage from inside a `page.evaluate` IIFE:
*
* ${BROWSER_JSON_SNIFF_FN}
* const res = await fetchJsonOrLoginWall('/some/path.json', { credentials: 'include' });
* // res is the parsed JSON object, OR { __loginWall: true, status, url, contentType, bodyPreview }
* return res;
*View on GitHub (pinned to 49907e53dc)
Solutions
- Read the body[0..50] preview in the message to see what actually came back
- Retry the request — truncation is often transient (network/proxy)
- Log the full response body separately (status + text) when you need the complete payload
- If the endpoint consistently returns non-standard content, parse it manually instead of via parseJsonOrThrowLoginWall
Example fix
// before
const data = JSON.parse(text); // opaque SyntaxError
// after
try { const data = JSON.parse(text); }
catch (e) { console.error('status', res.status, 'body:', text.slice(0, 200)); throw e; } Defensive patterns
Strategy: retry
Validate before calling
const text = await res.text();
if (!text.trim()) throw new Error(`empty body (status ${res.status}) — retry`); Try / catch
try {
const data = await parseJsonOrThrowLoginWall(res, { url });
} catch (e) {
const m = /JSON parse failed \(status=(\d+)/.exec(e.message);
if (m) { console.error(`Malformed JSON from ${url} (status ${m[1]}) — inspect body preview in message`); }
throw e;
} Prevention
- Retry transient truncations with backoff before failing a batch
- Capture and log the raw response text on parse failure
- Watch for endpoints changing response formats (JSONP/NDJSON) after API updates
- Set adequate timeouts so large bodies are not cut off mid-stream
When it happens
Trigger: The endpoint returned a 200 (or non-HTML error) response whose body is truncated, empty, partially streamed, or is JSON-ish text with a syntax error — JSON.parse throws inside the try block of parseJsonOrThrowLoginWall.
Common situations: Server-side errors that emit empty bodies with 200; proxies/CDNs truncating large responses; endpoints that changed their response shape (JSONP, NDJSON, or concatenated JSON); timeouts mid-body on flaky networks.
Related errors
- mdn search returned malformed JSON: ${err?.message ?? err}
- archive snapshots returned malformed JSON: ${error?.message
- Chess.com callback returned malformed JSON for ${url}: ${err
- `${label} returned malformed JSON: ${err?.message ?? err}`
- ${label} returned malformed JSON: ${err?.message ?? err}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/24df3dbc9120354d.
Report an issue: GitHub.