{"record":{"id":"efda0b705fae88f3","repo":"jackwener/OpenCLI","slug":"label-returned-malformed-json-err-message-efda0b","errorCode":null,"errorMessage":"${label} returned malformed JSON: ${err?.message ?? err}","messagePattern":"(.+?) returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/lichess/utils.js","lineNumber":86,"sourceCode":"    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `Lichess returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'Lichess throttles anonymous traffic at ~60 req/min; back off and retry.',\n        );\n    }\n    if (!resp.ok) {\n        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);\n    }\n    let body;\n    try {\n        body = await resp.json();\n    }\n    catch (err) {\n        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);\n    }\n    return body;\n}\n\n/** Format a lichess unix-ms timestamp as ISO date (YYYY-MM-DD). `null` when missing. */\nexport function formatTimestamp(ms) {\n    if (typeof ms !== 'number' || !Number.isFinite(ms) || ms <= 0) return null;\n    const d = new Date(ms);\n    if (Number.isNaN(d.getTime())) return null;\n    return d.toISOString();\n}\n","sourceCodeStart":68,"sourceCodeEnd":98,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/lichess/utils.js#L68-L98","documentation":"After a successful HTTP response, lichessFetch parses the body with resp.json(). If Lichess returns non-JSON content (HTML error page, empty body, CDN block page), JSON.parse fails and this CommandExecutionError is thrown with the underlying parse message. It distinguishes 'server answered but not with JSON' from HTTP-level failures.","triggerScenarios":"resp.json() rejects inside lichessFetch — e.g. an HTML 502 page from a reverse proxy, a Cloudflare challenge page, or an empty response body from a network middlebox, all with a 2xx/ok status.","commonSituations":"Corporate proxy or VPN intercepting traffic to lichess.org; captive portal Wi-Fi returning HTML; intermittent Lichess CDN issues; antivirus/proxy TLS inspection.","solutions":["Print/inspect the raw response (curl -v the same URL) to see what non-JSON content is returned.","Check for proxy/VPN/captive-portal interference and bypass it.","Retry the command — often transient.","If persistent, report to Lichess or pin to a different network."],"exampleFix":"// before\nconst user = await lichessFetch('/api/user/foo');\n// after\nlet user;\ntry {\n  user = await lichessFetch('/api/user/foo');\n} catch (e) {\n  if (String(e.message).includes('malformed JSON')) {\n    console.error('Lichess returned non-JSON (proxy or outage?) — retrying');\n    user = await lichessFetch('/api/user/foo');\n  } else throw e;\n}","handlingStrategy":"retry","validationCode":"// Pre-flight: confirm the endpoint returns JSON\nconst res = await fetch('https://lichess.org/api/user/' + encodeURIComponent(user), { headers: { accept: 'application/json' } });\nconst ct = res.headers.get('content-type') || '';\nif (!ct.includes('application/json')) throw new Error('non-JSON response — check proxy/network');","typeGuard":"function isJsonObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }","tryCatchPattern":"try {\n  const data = await lichessFetch(path);\n} catch (e) {\n  if (e.message.includes('malformed JSON')) {\n    // transient/proxy issue: inspect raw response and retry once\n    return retryWithBackoff(() => lichessFetch(path), 1);\n  }\n  throw e;\n}","preventionTips":["Set Accept: application/json on all requests.","Bypass suspicious proxies/VPNs when calling lichess.org.","Retry once on parse errors — they are often transient middlebox artifacts.","Log the raw response body when debugging to see the HTML interstitial."],"tags":["json","network","parsing"],"backgroundTag":"malformed-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}