{"record":{"id":"aa3335411b5995d7","repo":"jackwener/OpenCLI","slug":"label-returned-malformed-json-err-message-aa3335","errorCode":null,"errorMessage":"${label} returned malformed JSON: ${err?.message ?? err}","messagePattern":"(.+?) returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/wikidata/utils.js","lineNumber":89,"sourceCode":"    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `Wikidata returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'Wikidata throttles anonymous traffic; 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/**\n * Pick a localised label / description from a `{<lang>: {value}}` map.\n * Falls back to English if the requested language is missing.\n */\nexport function pickLocalised(map, language) {\n    if (!map || typeof map !== 'object') return null;\n    const direct = map[language];\n    if (direct && typeof direct.value === 'string' && direct.value.trim()) return direct.value;\n    if (language !== 'en' && map.en && typeof map.en.value === 'string' && map.en.value.trim()) {\n        return map.en.value;\n    }\n    return null;\n}\n","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/wikidata/utils.js#L71-L107","documentation":"After a 2xx response, wikidataFetch calls resp.json(). If the body cannot be parsed as JSON (truncated response, HTML error page from an intermediary, wrong content-type), it throws a CommandExecutionError noting the label and the underlying parse error.","triggerScenarios":"An intermediary (proxy, captive portal, anti-bot page) returning HTML instead of the JSON API response; the connection dropping mid-body so JSON is truncated; a misconfigured URL pointing to a non-API endpoint that returns text/HTML.","commonSituations":"Corporate proxies or Wi-Fi captive portals injecting HTML login pages; inconsistent networking in containers/CI truncating large responses; fetching through a caching layer that serves an error page with 200.","solutions":["Log resp status and a snippet of the raw text to see what was actually returned","Verify network path — bypass proxies/VPNs and retry","Retry the request; truncated responses are often transient","Confirm the URL passed to wikidataFetch is a Wikidata API endpoint (.json / api.php)"],"exampleFix":"// before\nconst body = await wikidataFetch(url, 'entity');\n// after\ntry {\n  const body = await wikidataFetch(url, 'entity');\n} catch (err) {\n  if (String(err.message).includes('malformed JSON')) {\n    const raw = await fetch(url); console.log(await raw.text()); // inspect actual body\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isJsonObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }","tryCatchPattern":"try {\n  const body = await wikidataFetch(url, label);\n} catch (err) {\n  if (String(err.message).includes('malformed JSON')) {\n    console.error('non-JSON body — check proxy/captive portal; retrying may help');\n    throw err;\n  }\n  throw err;\n}","preventionTips":["Inspect raw response bodies when behind proxies/VPN","Retry on transient truncation","Verify the URL is a real Wikidata JSON endpoint","Keep anti-virus/HTTPS-interception software from rewriting API traffic"],"tags":["json","parse-error","wikidata","network"],"backgroundTag":"malformed-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}