jackwener/OpenCLI · error · CommandExecutionError
coingecko returned malformed JSON: ${error?.message || error
Error message
coingecko returned malformed JSON: ${error?.message || error} What it means
This CommandExecutionError is thrown when the /coins/markets response body cannot be parsed as JSON, even though an HTTP response arrived. The library wraps resp.json() failures so the underlying parse error is preserved in the message. It usually means the body was HTML (Cloudflare/blocked page) or truncated.
Source
Thrown at clis/coingecko/top.js:46
const url = new URL('https://api.coingecko.com/api/v3/coins/markets');
url.searchParams.set('vs_currency', currency);
url.searchParams.set('order', 'market_cap_desc');
url.searchParams.set('per_page', String(limit));
url.searchParams.set('page', '1');
url.searchParams.set('sparkline', 'false');
let resp;
try {
resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
} catch (error) {
throw new CommandExecutionError(`coingecko top request failed: ${error?.message || error}`);
}
if (!resp.ok) throw new CommandExecutionError(`coingecko top failed: HTTP ${resp.status}`);
let data;
try {
data = await resp.json();
} catch (error) {
throw new CommandExecutionError(`coingecko returned malformed JSON: ${error?.message || error}`);
}
if (data?.error) throw new CommandExecutionError(`coingecko returned error: ${data.error}`);
if (!Array.isArray(data)) throw new CommandExecutionError('coingecko returned an unexpected response');
if (data.length === 0) throw new EmptyResultError('coingecko top', 'coingecko returned no market data');
return data.map((c) => ({
rank: c.market_cap_rank,
symbol: String(c.symbol ?? '').toUpperCase(),
name: c.name,
price: c.current_price,
change24hPct: c.price_change_percentage_24h,
marketCap: c.market_cap,
volume24h: c.total_volume,
high24h: c.high_24h,
low24h: c.low_24h,
}));
},
});View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the raw response with curl to confirm whether it is HTML or truncated JSON.
- Retry with backoff — often transient.
- Use a CoinGecko Pro API endpoint/key or a different exit IP if Cloudflare keeps challenging.
- Disable or bypass interfering proxies/SSL inspection for api.coingecko.com.
- Add a browser-like User-Agent/Accept headers if your environment strips them.
Example fix
// before
const rows = await runCli('coingecko', 'top');
// after
let rows; try { rows = await runCli('coingecko', 'top'); }
catch (e) {
if (String(e.message).includes('malformed JSON')) { await sleep(2000); rows = await runCli('coingecko', 'top'); }
else throw e;
} Defensive patterns
Strategy: retry
Validate before calling
const r = await fetch('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&per_page=1', { headers: { 'User-Agent': 'Mozilla/5.0' } });
const ct = r.headers.get('content-type') || '';
if (!ct.includes('application/json')) throw new Error('coingecko not returning JSON (got ' + ct + ')'); Type guard
function isJsonContentType(resp) { return (resp.headers.get('content-type') || '').includes('application/json'); } Try / catch
try {
rows = await runCli('coingecko', 'top');
} catch (e) {
if (String(e.message).includes('malformed JSON')) {
await new Promise(r => setTimeout(r, 3000));
rows = await runCli('coingecko', 'top');
} else throw e;
} Prevention
- Check content-type before parsing JSON responses
- Use Pro endpoints/keys to reduce Cloudflare challenges
- Avoid datacenter/VPN exit IPs known to be flagged
- Add request spacing so bot protection is not triggered
When it happens
Trigger: Cloudflare returning an HTML challenge with 200; a captive portal or proxy injecting HTML; response body truncated mid-transfer; CoinGecko returning an empty body on some edge errors.
Common situations: Datacenter/VPN IPs flagged by Cloudflare; corporate SSL-inspection proxies rewriting bodies; flaky mobile networks truncating responses; scripted bursts triggering bot protection.
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
- 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 ??
- lobsters domain returned malformed JSON: ${err?.message ?? e
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/2d235687cf57fe67.
Report an issue: GitHub.