jackwener/OpenCLI · error · CommandExecutionError
${label} returned malformed JSON: ${err?.message ?? err}
Error message
${label} returned malformed JSON: ${err?.message ?? err} What it means
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.
Source
Thrown at clis/openalex/utils.js:116
'OpenAlex throttles unauthenticated traffic; wait a few seconds and retry, or set OPENALEX_MAILTO.',
);
}
if (!resp.ok) {
let detail = '';
try {
const text = await resp.text();
const match = text.match(/"message"\s*:\s*"([^"]+)"/);
if (match) detail = ` (${match[1]})`;
}
catch { /* ignore */ }
throw new CommandExecutionError(`${label} returned HTTP ${resp.status}${detail}`);
}
let body;
try {
body = await resp.json();
}
catch (err) {
throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
}
return body;
}
/** Strip the `https://openalex.org/` prefix if present so columns surface just the bare id. */
export function bareId(value) {
const s = String(value ?? '').trim();
if (!s) return '';
return s.replace(/^https?:\/\/(?:api\.)?openalex\.org\//i, '').replace(/^works\//i, '');
}
/** Strip the `https://doi.org/` prefix so DOIs render as plain `10.…/…` strings. */
export function bareDoi(value) {
const s = String(value ?? '').trim();
if (!s) return '';
return s.replace(/^https?:\/\/(?:dx\.)?doi\.org\//i, '');
}
View on GitHub (pinned to 49907e53dc)
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.
Example fix
// before
const body = await openalexFetch(url, 'openalex works'); // throws on HTML body
// after
try {
const body = await openalexFetch(url, 'openalex works');
} catch (e) {
const raw = await fetch(url).then(r => r.text());
console.error('raw body head:', raw.slice(0, 200));
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
async function isReachableApi() {
try {
const r = await fetch('https://api.openalex.org/works?per-page=1');
return /json/.test(r.headers.get('content-type') ?? '');
} catch { return false; }
} Type guard
function isJsonObject(v) {
return v !== null && typeof v === 'object' && !Array.isArray(v);
} Try / catch
try {
const body = await openalexFetch(url, 'openalex works');
} catch (e) {
if (/malformed JSON/.test(e.message)) {
// inspect raw body, check proxy/captive portal, retry once
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ${label} returned malformed JSON: ${err?.message ?? err}
- Malformed JSON from Stack Exchange API for ${label}: ${detai
- stack exchange returned malformed JSON: ${error?.message ||
- Xiaoyuzhou refresh returned invalid JSON: ${getErrorMessage(
- 12306 ${endpoint} returned an unexpected payload shape
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/0b0bf0fcbfe8f038.
Report an issue: GitHub.