{"record":{"id":"2d235687cf57fe67","repo":"jackwener/OpenCLI","slug":"coingecko-returned-malformed-json-error-messag-2d2356","errorCode":null,"errorMessage":"coingecko returned malformed JSON: ${error?.message || error}","messagePattern":"coingecko returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/coingecko/top.js","lineNumber":46,"sourceCode":"    const url = new URL('https://api.coingecko.com/api/v3/coins/markets');\n    url.searchParams.set('vs_currency', currency);\n    url.searchParams.set('order', 'market_cap_desc');\n    url.searchParams.set('per_page', String(limit));\n    url.searchParams.set('page', '1');\n    url.searchParams.set('sparkline', 'false');\n\n    let resp;\n    try {\n      resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });\n    } catch (error) {\n      throw new CommandExecutionError(`coingecko top request failed: ${error?.message || error}`);\n    }\n    if (!resp.ok) throw new CommandExecutionError(`coingecko top failed: HTTP ${resp.status}`);\n    let data;\n    try {\n      data = await resp.json();\n    } catch (error) {\n      throw new CommandExecutionError(`coingecko returned malformed JSON: ${error?.message || error}`);\n    }\n    if (data?.error) throw new CommandExecutionError(`coingecko returned error: ${data.error}`);\n    if (!Array.isArray(data)) throw new CommandExecutionError('coingecko returned an unexpected response');\n    if (data.length === 0) throw new EmptyResultError('coingecko top', 'coingecko returned no market data');\n\n    return data.map((c) => ({\n      rank: c.market_cap_rank,\n      symbol: String(c.symbol ?? '').toUpperCase(),\n      name: c.name,\n      price: c.current_price,\n      change24hPct: c.price_change_percentage_24h,\n      marketCap: c.market_cap,\n      volume24h: c.total_volume,\n      high24h: c.high_24h,\n      low24h: c.low_24h,\n    }));\n  },\n});","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/coingecko/top.js#L28-L64","documentation":"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.","triggerScenarios":"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.","commonSituations":"Datacenter/VPN IPs flagged by Cloudflare; corporate SSL-inspection proxies rewriting bodies; flaky mobile networks truncating responses; scripted bursts triggering bot protection.","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."],"exampleFix":"// before\nconst rows = await runCli('coingecko', 'top');\n// after\nlet rows; try { rows = await runCli('coingecko', 'top'); }\ncatch (e) {\n  if (String(e.message).includes('malformed JSON')) { await sleep(2000); rows = await runCli('coingecko', 'top'); }\n  else throw e;\n}","handlingStrategy":"retry","validationCode":"const r = await fetch('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&per_page=1', { headers: { 'User-Agent': 'Mozilla/5.0' } });\nconst ct = r.headers.get('content-type') || '';\nif (!ct.includes('application/json')) throw new Error('coingecko not returning JSON (got ' + ct + ')');","typeGuard":"function isJsonContentType(resp) { return (resp.headers.get('content-type') || '').includes('application/json'); }","tryCatchPattern":"try {\n  rows = await runCli('coingecko', 'top');\n} catch (e) {\n  if (String(e.message).includes('malformed JSON')) {\n    await new Promise(r => setTimeout(r, 3000));\n    rows = await runCli('coingecko', 'top');\n  } else throw e;\n}","preventionTips":["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"],"tags":["network","json-parsing","cloudflare","http"],"backgroundTag":"malformed-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}