{"record":{"id":"24df3dbc9120354d","repo":"jackwener/OpenCLI","slug":"json-parse-failed-status-response-status-body","errorCode":null,"errorMessage":"JSON parse failed (status=${response.status}, body[0..50]=${JSON.stringify(trimmed.slice(0, 50))}): ${(err instanceof Error ? err.message : String(err))}","messagePattern":"JSON parse failed \\(status=(.+?), body\\[0\\.\\.50\\]=(.+?)\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils.ts","lineNumber":162,"sourceCode":"    contentType.toLowerCase().includes('text/html')\n    || /^<(?:!doctype|html|head|body|title)(?:[\\s>/]|$)/i.test(trimmed);\n\n  if (looksLikeHtml) {\n    throw new LoginWallError(\n      `Server returned HTML instead of JSON (status=${response.status}). `\n      + `Likely a login wall, rate limit, or WAF challenge.`,\n      response.status,\n      opts.url || response.url || '',\n      trimmed.slice(0, 100),\n    );\n  }\n\n  try {\n    return JSON.parse(text);\n  } catch (err) {\n    // Real malformed JSON \\u2014 surface body preview alongside the parser message\n    // so we don't have to repro to know what came back.\n    throw new Error(\n      `JSON parse failed (status=${response.status}, body[0..50]=${JSON.stringify(trimmed.slice(0, 50))}): `\n      + (err instanceof Error ? err.message : String(err)),\n    );\n  }\n}\n\n/** Browser-side JS source fragment (as a string) that performs a `fetch` and\n * either returns the parsed JSON body or a `LoginWallSignal` sentinel when\n * the response is HTML. Intended to be embedded inside an adapter's\n * `page.evaluate` block.\n *\n * Usage from inside a `page.evaluate` IIFE:\n *\n *     ${BROWSER_JSON_SNIFF_FN}\n *     const res = await fetchJsonOrLoginWall('/some/path.json', { credentials: 'include' });\n *     // res is the parsed JSON object, OR { __loginWall: true, status, url, contentType, bodyPreview }\n *     return res;\n *","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/utils.ts#L144-L180","documentation":"When the response body genuinely is not HTML but JSON.parse still fails, parseJsonOrThrowLoginWall throws a plain Error embedding the HTTP status, the first 50 chars of the body (JSON-stringified), and the underlying parser message. This gives you the actual malformed payload inline so you don't need to reproduce the request to debug it.","triggerScenarios":"The endpoint returned a 200 (or non-HTML error) response whose body is truncated, empty, partially streamed, or is JSON-ish text with a syntax error — JSON.parse throws inside the try block of parseJsonOrThrowLoginWall.","commonSituations":"Server-side errors that emit empty bodies with 200; proxies/CDNs truncating large responses; endpoints that changed their response shape (JSONP, NDJSON, or concatenated JSON); timeouts mid-body on flaky networks.","solutions":["Read the body[0..50] preview in the message to see what actually came back","Retry the request — truncation is often transient (network/proxy)","Log the full response body separately (status + text) when you need the complete payload","If the endpoint consistently returns non-standard content, parse it manually instead of via parseJsonOrThrowLoginWall"],"exampleFix":"// before\nconst data = JSON.parse(text); // opaque SyntaxError\n// after\ntry { const data = JSON.parse(text); }\ncatch (e) { console.error('status', res.status, 'body:', text.slice(0, 200)); throw e; }","handlingStrategy":"retry","validationCode":"const text = await res.text();\nif (!text.trim()) throw new Error(`empty body (status ${res.status}) — retry`);","typeGuard":null,"tryCatchPattern":"try {\n  const data = await parseJsonOrThrowLoginWall(res, { url });\n} catch (e) {\n  const m = /JSON parse failed \\(status=(\\d+)/.exec(e.message);\n  if (m) { console.error(`Malformed JSON from ${url} (status ${m[1]}) — inspect body preview in message`); }\n  throw e;\n}","preventionTips":["Retry transient truncations with backoff before failing a batch","Capture and log the raw response text on parse failure","Watch for endpoints changing response formats (JSONP/NDJSON) after API updates","Set adequate timeouts so large bodies are not cut off mid-stream"],"tags":["json","http","parse-error","network"],"backgroundTag":"json-parse-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}