jackwener/OpenCLI · error · CommandExecutionError
coingecko categories returned malformed JSON: ${err?.message
Error message
coingecko categories returned malformed JSON: ${err?.message ?? err} What it means
The HTTP response body could not be parsed as JSON; the CLI catches resp.json() failure and wraps it in a CommandExecutionError with the parse error. Typically the API returned HTML (error page, challenge page) instead of JSON.
Source
Thrown at clis/coingecko/categories.js:60
}
catch (err) {
throw new CommandExecutionError(`coingecko categories request failed: ${err?.message ?? err}`);
}
if (resp.status === 429) {
throw new CommandExecutionError(
'coingecko returned HTTP 429 (rate limited)',
'Free tier allows ~30 calls/min. Wait and retry.',
);
}
if (!resp.ok) {
throw new CommandExecutionError(`coingecko categories returned HTTP ${resp.status}`);
}
let data;
try {
data = await resp.json();
}
catch (err) {
throw new CommandExecutionError(`coingecko categories returned malformed JSON: ${err?.message ?? err}`);
}
if (!Array.isArray(data) || !data.length) {
throw new EmptyResultError('coingecko categories', 'CoinGecko returned no category data.');
}
return data.slice(0, limit).map((cat, i) => ({
rank: i + 1,
id: String(cat.id ?? ''),
name: String(cat.name ?? ''),
marketCap: cat.market_cap != null ? Number(cat.market_cap) : null,
volume24h: cat.volume_24h != null ? Number(cat.volume_24h) : null,
marketCapChange24hPct: cat.market_cap_change_24h != null ? Number(cat.market_cap_change_24h) : null,
top3Coins: Array.isArray(cat.top_3_coins_id) ? cat.top_3_coins_id.join(', ') : '',
}));
},
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Retry after a delay — often transient
- Check whether a proxy/CDN is intercepting the response and bypass it
- Inspect the raw response (curl the URL) to see what body is actually returned
Defensive patterns
Strategy: try-catch
Validate before calling
// verify the endpoint returns JSON before relying on it
const probe = await fetch('https://api.coingecko.com/api/v3/coins/categories?limit=1');
const ct = probe.headers.get('content-type') || '';
if (!ct.includes('application/json')) throw new Error('CoinGecko not returning JSON'); Type guard
function isJsonArray(v) {
return Array.isArray(v);
} Try / catch
try {
await runCategories(args);
} catch (err) {
if (err instanceof CommandExecutionError && /malformed JSON/.test(err.message)) {
await sleep(5000);
return runCategories(args); // often a transient HTML error page
}
throw err;
} Prevention
- Check content-type before parsing JSON in custom clients
- Bypass intercepting proxies/VPN that inject HTML pages
- Space out requests to avoid Cloudflare-style challenges
When it happens
Trigger: resp.json() throws because the body is HTML/text — e.g. a Cloudflare challenge, gateway error page, or truncated response from api.coingecko.com.
Common situations: CoinGecko outage returning an HTML error page; ISP/proxy interception page; CDN challenge (Cloudflare) triggered by rate or bot detection.
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
- ${label} returned malformed JSON: ${err?.message ?? err}
- ${errorPrefix ? `${errorPrefix}: ` : ''}Boss API returned ma
- Boss API request failed: ${message}
- Chess.com API returned malformed JSON for ${url}: ${error?.m
- coingecko categories request failed: ${err?.message ?? err}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/020e573504c48475.
Report an issue: GitHub.