jackwener/OpenCLI · error · CommandExecutionError
${label} returned invalid JSON
Error message
${label} returned invalid JSON What it means
eutilsFetch wraps NCBI E-utilities HTTP calls and parses the JSON body when retmode is JSON. If response.json() throws (and the failure is not already a CommandExecutionError from assertNoEutilsError), it rethrows as `${label} returned invalid JSON` with the parse error as detail. The library throws this because it cannot distinguish an API problem from corrupted/non-JSON output, so it fails loudly instead of returning undefined data.
Source
Thrown at clis/pubmed/utils.js:121
throw new CommandExecutionError(`${label} request failed`, detail);
}
if (!response.ok) {
throw new CommandExecutionError(`${label} HTTP ${response.status}`, 'Check NCBI availability, request parameters, and optional NCBI_API_KEY.');
}
if (retmode === 'xml') {
return response.text();
}
try {
const json = await response.json();
assertNoEutilsError(json, label);
return json;
}
catch (error) {
if (error instanceof CommandExecutionError) {
throw error;
}
const detail = error instanceof Error ? error.message : String(error);
throw new CommandExecutionError(`${label} returned invalid JSON`, detail);
}
}
export function assertNoEutilsError(json, label = 'PubMed E-utilities') {
const error = json?.error
|| json?.esearchresult?.errorlist?.phrasesnotfound?.join(', ')
|| json?.esearchresult?.errorlist?.fieldsnotfound?.join(', ');
if (error) {
throw new CommandExecutionError(`${label} returned an error`, String(error));
}
}
export function buildPubMedUrl(pmid) {
return `https://pubmed.ncbi.nlm.nih.gov/${pmid}/`;
}
export function decodeXmlEntities(value) {
return String(value ?? '')View on GitHub (pinned to 49907e53dc)
Solutions
- Retry the call after a short delay; NCBI rate limits (3 req/s without API key) commonly trigger HTML throttle pages
- Request a free NCBI_API_KEY and set it to raise the rate limit to 10 req/s
- Check NCBI E-utilities status and network path (proxy/VPN) that may inject HTML
- Inspect the `detail` field of the CommandExecutionError to see the exact JSON parse failure
Example fix
// before
const json = await esearch(query);
// after
let json;
try {
json = await esearch(query);
} catch (e) {
if (/returned invalid JSON/.test(e.message)) {
await new Promise(r => setTimeout(r, 1000)); // back off, then retry
json = await esearch(query);
} else throw e;
} Defensive patterns
Strategy: retry
Validate before calling
// Validate the endpoint reachability before parsing
const res = await fetch(url);
const ct = res.headers.get('content-type') || '';
if (!ct.includes('application/json')) throw new Error(`Expected JSON, got ${ct}`); Type guard
function isJsonObject(v) {
return v !== null && typeof v === 'object' && !Array.isArray(v);
} Try / catch
try {
const json = await esearch(query);
} catch (e) {
if (/returned invalid JSON/.test(e.message)) {
await backoff(1000);
return esearch(query); // retry once
}
throw e;
} Prevention
- Set NCBI_API_KEY to avoid throttle responses that arrive as HTML
- Check the response content-type before trusting JSON parsing
- Add backoff/retry around E-utilities calls (respect 3 req/s limit)
- Log the raw body on parse failure to detect proxies injecting HTML
When it happens
Trigger: Calling esearch, esummary/result, or eutilsFetch against an NCBI endpoint whose 200 response body is not valid JSON — e.g. an HTML error/interstitial page, a rate-limit or maintenance page, a proxy/captive portal injecting HTML, or a truncated response.
Common situations: NCBI throttling without an NCBI_API_KEY returning HTML instead of JSON; corporate proxies or VPNs rewriting responses; NCBI service outages; network middleware returning empty bodies with 200 status.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- 12306 queryByTrainNo returned non-JSON body
- 12306 ${endpoint} returned non-JSON body
- coingecko derivatives returned malformed JSON: ${err?.messag
- coingecko global returned malformed JSON: ${err?.message ??
- coingecko returned malformed JSON: ${error?.message || error
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/4a9a4ae12c1da3cb.
Report an issue: GitHub.