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 CoinGecko trending response body cannot be parsed as JSON — resp.json() rejects. The library wraps the parse error and prefixes context so it's clear which endpoint returned the malformed body. Commonly this means the server returned an HTML page (Cloudflare challenge, error page, proxy block page) instead of JSON.
Source
Thrown at clis/coingecko/trending.js:36
func: async () => {
const url = 'https://api.coingecko.com/api/v3/search/trending';
let resp;
try {
resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
} catch (error) {
throw new CommandExecutionError(`coingecko trending request failed: ${error?.message || error}`);
}
if (resp.status === 429) {
throw new CommandExecutionError('coingecko returned HTTP 429 (rate limited)', 'Wait and retry.');
}
if (!resp.ok) {
throw new CommandExecutionError(`coingecko trending failed: HTTP ${resp.status}`);
}
let data;
try {
data = await resp.json();
} catch (error) {
throw new CommandExecutionError(`coingecko returned malformed JSON: ${error?.message || error}`);
}
const coins = Array.isArray(data?.coins) ? data.coins : [];
if (coins.length === 0) {
throw new EmptyResultError('coingecko trending', 'coingecko returned no trending coins.');
}
return coins.map((entry, i) => {
const c = entry?.item || {};
return {
rank: i + 1,
id: c.id || '',
symbol: String(c.symbol || '').toUpperCase(),
name: c.name || '',
marketCapRank: c.market_cap_rank ?? null,
priceBtc: c.price_btc ?? null,
thumb: c.thumb || c.small || c.large || '',
};
});
},View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect what the endpoint actually returns: curl -s https://api.coingecko.com/api/v3/search/trending | head -c 200 — HTML output means bot protection
- Retry later or from a different network/IP if Cloudflare is challenging the spoofed Mozilla/5.0 User-Agent
- Use CoinGecko's official API (with demo key) for reliable JSON responses
- Add a defensive check in your wrapper: verify Content-Type is application/json before resp.json()
Example fix
// before
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
const data = await resp.json(); // may throw on HTML body
// after
const ct = resp.headers.get('content-type') || '';
if (!ct.includes('application/json')) throw new Error(`unexpected content-type: ${ct}`);
const data = await resp.json(); Defensive patterns
Strategy: try-catch
Validate before calling
const probe = await fetch('https://api.coingecko.com/api/v3/search/trending');
const ct = probe.headers.get('content-type') || '';
if (!ct.includes('application/json')) throw new Error(`non-JSON response: ${ct} — likely bot protection`); Type guard
function isJsonParseError(e) {
return /malformed JSON|Unexpected token|JSON parse/i.test(e?.message || '');
} Try / catch
try {
const data = await runCli('coingecko trending');
} catch (e) {
if (isJsonParseError(e)) {
const raw = await fetch(url).then(r => r.text());
console.error('non-JSON body:', raw.slice(0, 200)); // inspect HTML challenge/block page
}
throw e;
} Prevention
- If the body is HTML, you're being challenged by Cloudflare — use the official API with a key
- Verify content-type is application/json before parsing raw responses
- Retry from a different IP/network if blocked
- Keep library updated for API content-type changes
When it happens
Trigger: `await resp.json()` in the trending command throws a SyntaxError ('Unexpected token < in JSON', etc.) because the body of the HTTP 200 (or other ok) response is not valid JSON — e.g. an HTML bot-challenge page, an empty body, or a truncated response.
Common situations: Cloudflare/WAF serving an HTML challenge to the spoofed User-Agent request, corporate proxy injecting an HTML block page with status 200, network truncation mid-body, or CoinGecko API changes changing the content type.
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
- archive search returned malformed JSON: ${error?.message ||
- Chess.com API returned malformed JSON for ${url}: ${error?.m
- coingecko sort "${args.sort}" is not supported
- coingecko limit must be a positive integer
- coingecko limit must be <= 100
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/8d72aa882949d378.
Report an issue: GitHub.