{"record":{"id":"85e9e838cf7e850a","repo":"jackwener/OpenCLI","slug":"label-returned-malformed-json-err-message-85e9e8","errorCode":null,"errorMessage":"${label} returned malformed JSON: ${err?.message ?? err}","messagePattern":"(.+?) returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/packagist/utils.js","lineNumber":83,"sourceCode":"    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `Packagist returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'Packagist throttles bursts; wait a few seconds 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/** Trim \"2026-05-05T17:32:01+00:00\" → \"2026-05-05T17:32:01Z\" so timestamps are uniform. */\nexport function trimDate(value) {\n    const s = String(value ?? '').trim();\n    if (!s) return null;\n    const noFrac = s.replace(/\\.\\d+/, '');\n    return noFrac.replace(/(?:[+-]\\d{2}:?\\d{2}|Z)?$/, 'Z');\n}\n\n/**\n * Pick the newest stable (non-dev / non-prerelease) version key from a\n * Packagist `versions` map. Packagist returns keys ordered newest-first.\n * Falls back to the first key if no stable found.\n */\nexport function pickStableVersion(versions) {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/packagist/utils.js#L65-L101","documentation":"packagistFetch throws this when resp.json() fails to parse the HTTP response body. It means the server responded but the payload was not valid JSON (HTML error page, truncated body, redirect page). The underlying parse error message is appended for diagnosis.","triggerScenarios":"Packagist or an intermediary returns HTML (error/captcha/redirect page) with 200, or the response body is truncated/corrupted mid-transfer.","commonSituations":"Captive portals or proxies injecting HTML into responses; CDN error pages; network interruption truncating the body; hitting a mirror that returns non-JSON.","solutions":["Retry the request — transient truncation is common.","Check whether a proxy/firewall is intercepting and rewriting responses (look for HTML in the raw response).","Verify the API endpoint URL has not changed or been redirected.","Inspect err.message in the thrown error for the specific JSON parse failure."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const data = await body(pkg);\n} catch (err) {\n  if (/malformed JSON/.test(err.message)) {\n    // likely transient: retry with backoff\n    await sleep(2000);\n    return body(pkg);\n  }\n  throw err;\n}","preventionTips":["Retry once or twice with backoff before failing.","Bypass suspicious proxies when possible.","Log the raw response text on parse failure for diagnosis."],"tags":["json","network","packagist"],"backgroundTag":"malformed-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}