{"record":{"id":"22d14df7d4163a01","repo":"jackwener/OpenCLI","slug":"coingecko-derivatives-returned-malformed-json-e","errorCode":null,"errorMessage":"coingecko derivatives returned malformed JSON: ${err?.message ?? err}","messagePattern":"coingecko derivatives returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/coingecko/derivatives.js","lineNumber":57,"sourceCode":"        }\n        catch (err) {\n            throw new CommandExecutionError(`coingecko derivatives request failed: ${err?.message ?? err}`);\n        }\n        if (resp.status === 429) {\n            throw new CommandExecutionError(\n                'coingecko derivatives 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 derivatives returned HTTP ${resp.status}`);\n        }\n        let data;\n        try {\n            data = await resp.json();\n        }\n        catch (err) {\n            throw new CommandExecutionError(`coingecko derivatives returned malformed JSON: ${err?.message ?? err}`);\n        }\n        if (!Array.isArray(data) || !data.length) {\n            throw new EmptyResultError('coingecko derivatives', 'CoinGecko returned no derivative tickers.');\n        }\n        let rows = data;\n        if (filter) {\n            rows = data.filter((d) => String(d.symbol ?? '').toUpperCase().includes(filter)\n                || String(d.index_id ?? '').toUpperCase().includes(filter));\n            if (!rows.length) {\n                throw new EmptyResultError('coingecko derivatives', `No derivative tickers matched symbol=\"${filter}\".`);\n            }\n        }\n        return rows.slice(0, limit).map((d, i) => ({\n            rank: i + 1,\n            market: String(d.market ?? ''),\n            symbol: String(d.symbol ?? ''),\n            indexId: String(d.index_id ?? ''),\n            contractType: String(d.contract_type ?? ''),","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/coingecko/derivatives.js#L39-L75","documentation":"The coingecko derivatives command wraps the response body parse of GET https://api.coingecko.com/api/v3/derivatives in a try/catch. When resp.json() rejects (body is not valid JSON), it rethrows a CommandExecutionError with the underlying parse message. The library throws this because the HTTP request itself succeeded (status was ok), but the payload could not be decoded.","triggerScenarios":"CoinGecko returns HTTP 200 with a non-JSON body — e.g. an HTML Cloudflare challenge/interstitial page, a proxy/captive-portal interception page, a truncated or empty response body, or a maintenance error page served with a 2xx status.","commonSituations":"Corporate proxies or VPNs injecting an HTML block page; CoinGecko serving a Cloudflare 'checking your browser' page to the CLI's User-Agent; network middleboxes truncating the response mid-stream; CDN outage returning garbage.","solutions":["Retry the command after a short wait — transient CDN/proxy hiccups often resolve on their own.","Check the raw response with curl -s https://api.coingecko.com/api/v3/derivatives | head -c 200 to see what body is actually returned.","Disable or bypass intercepting proxies/VPNs (check HTTP(S)_PROXY env vars) that may inject HTML pages.","Reduce request frequency to avoid Cloudflare bot challenges; consider a CoinGecko Pro API key.","Verify network/DNS allows reaching api.coingecko.com directly."],"exampleFix":"// before — caller assumes success means JSON\nconst data = await runCli(['coingecko', 'derivatives']);\n// after\ntry {\n  const data = await runCli(['coingecko', 'derivatives']);\n} catch (err) {\n  if (String(err.message).includes('malformed JSON')) {\n    await sleep(1000); // transient HTML/Cloudflare page — retry\n    return retryDerivatives();\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"const resp = await fetch('https://api.coingecko.com/api/v3/derivatives');\nconst text = await resp.text();\nif (!resp.ok) throw new Error(`HTTP ${resp.status}`);\ntry { JSON.parse(text); } catch { console.warn('Non-JSON body, likely HTML challenge/proxy page'); }","typeGuard":"function isDerivativeArray(v) {\n  return Array.isArray(v) && v.every((d) => d != null && typeof d === 'object' && 'symbol' in d);\n}","tryCatchPattern":"try {\n  const rows = await runCli(['coingecko', 'derivatives']);\n} catch (err) {\n  if (String(err.message).includes('malformed JSON')) {\n    return retryWithBackoff(() => runCli(['coingecko', 'derivatives']), 3);\n  }\n  throw err;\n}","preventionTips":["Retry with backoff on malformed JSON — usually a transient HTML/CDN page.","Bypass suspicious proxies/VPNs; check HTTP(S)_PROXY env vars.","Keep request rate low to avoid Cloudflare bot challenges.","Inspect the raw body (curl) when it recurs to identify the interceptor."],"tags":["network","json-parsing","http","coingecko"],"backgroundTag":"malformed-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}