{"record":{"id":"f6a61e574cf1d6a2","repo":"jackwener/OpenCLI","slug":"label-returned-malformed-json-err-message-f6a61e","errorCode":null,"errorMessage":"${label} returned malformed JSON: ${err?.message ?? err}","messagePattern":"(.+?) returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/steam/utils.js","lineNumber":82,"sourceCode":"    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'Steam throttles bursty traffic; wait a few seconds and retry.',\n        );\n    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, 'Steam returned 404 — the resource does not exist.');\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\nexport function asString(value) {\n    return value == null ? '' : String(value);\n}\n\nconst HTML_ENTITIES = {\n    '&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '\"', '&apos;': \"'\", '&#39;': \"'\", '&nbsp;': ' ',\n};\n\nexport function decodeHtmlEntities(value) {\n    return String(value ?? '')\n        .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16)))\n        .replace(/&#(\\d+);/g, (_, d) => String.fromCodePoint(parseInt(d, 10)))\n        .replace(/&(amp|lt|gt|quot|apos|#39|nbsp);/g, (m) => HTML_ENTITIES[m] || m);\n}","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/steam/utils.js#L64-L100","documentation":"After a successful HTTP response, steamFetch parses the body as JSON. If resp.json() throws (non-JSON body such as HTML, empty body, or truncation), it throws a CommandExecutionError noting the response was malformed JSON.","triggerScenarios":"Steam returning an HTML error/challenge page with a 200 status, empty response bodies, interrupted/truncated responses, or an endpoint that returns text instead of JSON.","commonSituations":"Bot-protection interstitials served with 200, proxy software rewriting responses, network drops mid-body, calling an endpoint that returns CSV/HTML rather than JSON.","solutions":["Retry the request; truncation is often transient","Check the raw response with curl to see what Steam actually returned","Bypass rewriting proxies/VPN clients that may modify the body","Look for a bot-challenge page and switch to browser-session mode if available"],"exampleFix":"// before\nconst data = await steamFetch(url, 'app details'); // HTML body -> malformed JSON\n// after\nlet data;\ntry { data = await steamFetch(url, 'app details'); }\ncatch (e) { console.error(e.message); data = null; /* fall back to cached value */ }","handlingStrategy":"try-catch","validationCode":"const pre = await fetch(url, { headers: { accept: 'application/json' } });\nconst ct = pre.headers.get('content-type') || '';\nif (!ct.includes('json')) throw new Error('non-JSON response: ' + ct);","typeGuard":"function isJsonObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }","tryCatchPattern":"let data;\ntry { data = await steamFetch(url, label); }\ncatch (e) { if (/malformed JSON/.test(e.message)) { data = null; /* fall back to cache */ } else throw e; }","preventionTips":["Check content-type before parsing when doing raw fetches","Beware proxies that rewrite responses to HTML challenge pages","Retry once on parse failure; truncation is often transient","Validate parsed shape before use"],"tags":["json","http","steam"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}