{"record":{"id":"f4175a3e23f55a93","repo":"jackwener/OpenCLI","slug":"coingecko-exchanges-returned-malformed-json-err","errorCode":null,"errorMessage":"coingecko exchanges returned malformed JSON: ${err?.message ?? err}","messagePattern":"coingecko exchanges returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/coingecko/exchanges.js","lineNumber":58,"sourceCode":"        }\n        catch (err) {\n            throw new CommandExecutionError(`coingecko exchanges request failed: ${err?.message ?? err}`);\n        }\n        if (resp.status === 429) {\n            throw new CommandExecutionError(\n                'coingecko returned HTTP 429 (rate limited)',\n                'Free tier allows ~30 calls/min. Wait and retry.',\n            );\n        }\n        if (!resp.ok) {\n            throw new CommandExecutionError(`coingecko exchanges returned HTTP ${resp.status}`);\n        }\n        let data;\n        try {\n            data = await resp.json();\n        }\n        catch (err) {\n            throw new CommandExecutionError(`coingecko exchanges returned malformed JSON: ${err?.message ?? err}`);\n        }\n        if (!Array.isArray(data) || !data.length) {\n            throw new EmptyResultError('coingecko exchanges', 'CoinGecko returned no exchange data.');\n        }\n        return data.map((ex, i) => ({\n            rank: (page - 1) * limit + i + 1,\n            id: String(ex.id ?? ''),\n            name: String(ex.name ?? ''),\n            trustScore: ex.trust_score != null ? Number(ex.trust_score) : null,\n            volume24hBtc: ex.trade_volume_24h_btc != null ? Number(ex.trade_volume_24h_btc) : null,\n            country: String(ex.country ?? ''),\n            yearEstablished: ex.year_established != null ? Number(ex.year_established) : null,\n            url: String(ex.url ?? ''),\n        }));\n    },\n});\n","sourceCodeStart":40,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/coingecko/exchanges.js#L40-L75","documentation":"The library calls resp.json() on the CoinGecko exchanges response; if the body is not valid JSON (HTML error pages from proxies/Cloudflare, truncated responses, empty bodies), json() throws and this CommandExecutionError wraps that parse failure with the underlying SyntaxError message.","triggerScenarios":"The HTTP request succeeded (2xx) but resp.json() throws: response body is HTML (Cloudflare challenge page, captive portal), response is truncated, Content-Encoding mismatch (e.g. broken gzip via a proxy), or an empty body.","commonSituations":"Corporate proxies injecting HTML login pages, Cloudflare bot-challenge interstitials returned with a 200, flaky mobile/VPN connections truncating the body, or misconfigured local MITM proxies.","solutions":["Inspect the raw body with `curl -s https://api.coingecko.com/api/v3/exchanges?per_page=20&page=1 | head` to see what is actually returned (HTML challenge vs JSON).","If Cloudflare/proxy HTML is the cause, retry from a different network or use a proper API key plan that avoids challenge pages.","Retry the request — truncated bodies from flaky connections are usually transient.","Check for proxies (HTTP_PROXY/HTTPS_PROXY, corporate MITM) interfering with the response encoding."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await exchanges.func({ page: 1 });\n} catch (e) {\n  if (/malformed JSON/.test(e.message)) {\n    // body was HTML/truncated: verify raw response, retry or switch network\n    const raw = await fetch('https://api.coingecko.com/api/v3/exchanges?per_page=5&page=1').then(r => r.text());\n    if (raw.trimStart().startsWith('<')) handleCloudflareChallenge();\n  } else throw e;\n}","preventionTips":["Spot-check raw responses with curl when behind unfamiliar networks.","Disable or correctly configure proxies that rewrite HTML into responses.","Treat JSON parse failures on 2xx as a proxy/Cloudflare symptom, not a code bug.","Retry truncated responses — they are usually transient."],"tags":["json-parse","malformed-response","proxy"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}