{"record":{"id":"0b0bf0fcbfe8f038","repo":"jackwener/OpenCLI","slug":"label-returned-malformed-json-err-message-0b0bf0","errorCode":null,"errorMessage":"${label} returned malformed JSON: ${err?.message ?? err}","messagePattern":"(.+?) returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/openalex/utils.js","lineNumber":116,"sourceCode":"            'OpenAlex throttles unauthenticated traffic; wait a few seconds and retry, or set OPENALEX_MAILTO.',\n        );\n    }\n    if (!resp.ok) {\n        let detail = '';\n        try {\n            const text = await resp.text();\n            const match = text.match(/\"message\"\\s*:\\s*\"([^\"]+)\"/);\n            if (match) detail = ` (${match[1]})`;\n        }\n        catch { /* ignore */ }\n        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}${detail}`);\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/** Strip the `https://openalex.org/` prefix if present so columns surface just the bare id. */\nexport function bareId(value) {\n    const s = String(value ?? '').trim();\n    if (!s) return '';\n    return s.replace(/^https?:\\/\\/(?:api\\.)?openalex\\.org\\//i, '').replace(/^works\\//i, '');\n}\n\n/** Strip the `https://doi.org/` prefix so DOIs render as plain `10.…/…` strings. */\nexport function bareDoi(value) {\n    const s = String(value ?? '').trim();\n    if (!s) return '';\n    return s.replace(/^https?:\\/\\/(?:dx\\.)?doi\\.org\\//i, '');\n}\n","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/openalex/utils.js#L98-L134","documentation":"After a successful HTTP response, openalexFetch calls resp.json(); if the body is not parseable JSON it throws this CommandExecutionError. It guards against proxies, captive portals, or upstream outages returning HTML error pages instead of JSON.","triggerScenarios":"api.openalex.org (or an intercepting middlebox) returns a 200 response whose body is HTML or truncated — e.g. a Wi-Fi captive portal, a corporate proxy injecting a notice page, or a partially delivered response.","commonSituations":"Working behind a corporate proxy that rewrites responses; on public Wi-Fi where DNS is hijacked to a login page; a CDN/edge failure serving an HTML error with status 200.","solutions":["Print the first few hundred bytes of the raw response (curl the same URL) to confirm what is actually being returned.","Check whether a proxy or captive portal is intercepting api.openalex.org and bypass/authenticate it.","Retry the request; a truncated body is often transient.","Pin traffic to https and ensure no HTTP_PROXY env var is rewriting openalex requests unexpectedly."],"exampleFix":"// before\nconst body = await openalexFetch(url, 'openalex works'); // throws on HTML body\n// after\ntry {\n  const body = await openalexFetch(url, 'openalex works');\n} catch (e) {\n  const raw = await fetch(url).then(r => r.text());\n  console.error('raw body head:', raw.slice(0, 200));\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"async function isReachableApi() {\n  try {\n    const r = await fetch('https://api.openalex.org/works?per-page=1');\n    return /json/.test(r.headers.get('content-type') ?? '');\n  } catch { return false; }\n}","typeGuard":"function isJsonObject(v) {\n  return v !== null && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  const body = await openalexFetch(url, 'openalex works');\n} catch (e) {\n  if (/malformed JSON/.test(e.message)) {\n    // inspect raw body, check proxy/captive portal, retry once\n  } else throw e;\n}","preventionTips":["Check content-type is application/json before parsing when fetching manually.","Bypass corporate proxies or configure NO_PROXY for api.openalex.org.","Authenticate captive-portal networks before batch runs.","Retry once on malformed JSON — truncation is often transient."],"tags":["json","network","proxy","openalex"],"backgroundTag":"malformed-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}