jackwener/OpenCLI · error · CommandExecutionError
Chess.com callback returned malformed JSON for ${url}: ${err
Error message
Chess.com callback returned malformed JSON for ${url}: ${error?.message || error} What it means
When resp.ok is true but resp.json() throws, the library rethrows as CommandExecutionError indicating the callback returned malformed JSON. Chess.com's callback endpoint normally returns JSON, but error pages, HTML challenges, or truncated responses will fail to parse.
Source
Thrown at clis/chess/game.js:108
try {
resp = await fetch(url, { headers: { 'User-Agent': UA, accept: 'application/json' } });
} catch (error) {
throw new CommandExecutionError(`Failed to fetch Chess.com callback ${url}: ${error?.message || error}`);
}
if (!resp || typeof resp !== 'object') {
throw new CommandExecutionError(`Chess.com callback returned an invalid response object for ${url}`);
}
if (resp.status === 404) {
throw new EmptyResultError(`Chess.com has no ${kind} game with id ${id}`);
}
if (!resp.ok) {
throw new CommandExecutionError(`Chess.com callback returned HTTP ${resp.status} for ${url}`);
}
let payload;
try {
payload = await resp.json();
} catch (error) {
throw new CommandExecutionError(`Chess.com callback returned malformed JSON for ${url}: ${error?.message || error}`);
}
return [summarizeGame({ kind, id, payload })];
},
});
export const __test__ = { parseGameUrl, summarizeGame };
View on GitHub (pinned to 49907e53dc)
Solutions
- Log or print the raw response text to see what was actually returned (wrap fetch yourself or use curl with the same User-Agent)
- Retry after a delay — truncated responses are often transient
- Switch to the documented public API (api.chess.com/pub/game/{id}) if the callback endpoint keeps returning HTML
- Check for proxy/firewall interference on the host machine
Example fix
// before: silent assumption of JSON
const payload = await resp.json();
// after: capture body for diagnosis
const text = await resp.text();
let payload;
try { payload = JSON.parse(text); }
catch (e) { console.error('Non-JSON body:', text.slice(0, 200)); throw e; } Defensive patterns
Strategy: try-catch
Try / catch
try {
const rows = await chessGameCmd(gameUrl);
} catch (e) {
if (/malformed JSON/.test(e.message)) {
// retry once after delay; if persistent, capture raw body via curl for diagnosis
await new Promise(r => setTimeout(r, 3000));
return await chessGameCmd(gameUrl);
}
throw e;
} Prevention
- Retry transient parse failures after a short delay
- Diagnose with curl using the same User-Agent to see the raw body
- Watch for Cloudflare/HTML challenge pages served with status 200
- Bypass suspicious proxies/VPNs when the endpoint returns HTML
When it happens
Trigger: The /callback/{kind}/game/{id} endpoint returns 200 with a non-JSON body: an HTML bot-challenge page, an empty body, a truncated response, or a content-type change by Chess.com.
Common situations: Cloudflare/bot interstitial served with status 200; network middleboxes (corporate proxies) rewriting responses; Chess.com changing the callback endpoint format; flaky connections truncating the body.
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
- mdn search returned malformed JSON: ${err?.message ?? err}
- ${label} 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/f1aa8cd79560d379.
Report an issue: GitHub.