{"record":{"id":"d54ccb645c1a7fd1","repo":"jackwener/OpenCLI","slug":"coingecko-exchanges","errorCode":null,"errorMessage":"coingecko exchanges","messagePattern":"coingecko exchanges","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"info","filePath":"clis/coingecko/exchanges.js","lineNumber":61,"sourceCode":"        }\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":43,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/coingecko/exchanges.js#L43-L75","documentation":"This EmptyResultError is thrown when CoinGecko's /exchanges response parsed successfully but is not a non-empty array — i.e. the API returned `[]`, null, or an unexpected shape, meaning no exchange data exists for this request. It signals a legitimate empty/empty-ish result rather than a transport failure.","triggerScenarios":"Requesting a page far beyond the last page of results (e.g. page=50 when only ~3 pages of exchanges exist), so CoinGecko returns an empty array; or the API changes its response shape from array to object/envelope.","commonSituations":"Pagination loops that don't stop when results run out, computing page numbers from stale total counts, or scripts assuming infinite pages.","solutions":["Stop paginating when this error occurs — you've passed the last page; use page 1..N where N is small (CoinGecko lists a few hundred exchanges).","Increase per-page size (limit up to 250) to get everything in fewer pages, often a single page=1 call.","Handle EmptyResultError in your wrapper as a normal end-of-data condition, not a failure.","If the shape changed (API version change), check CoinGecko changelog and update the command."],"exampleFix":"// before\nfor (let p = 1; ; p++) await exchanges.func({ page: p });\n// after\nfor (let p = 1; p <= 10; p++) {\n  try { await exchanges.func({ page: p }); }\n  catch (e) { if (e.name === 'EmptyResultError') break; throw e; }\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const rows = await exchanges.func({ page });\n} catch (e) {\n  if (e.name === 'EmptyResultError') return []; // normal end-of-pagination\n  throw e;\n}","preventionTips":["Stop pagination on EmptyResultError instead of treating it as a crash.","Fetch up to 250 per page; CoinGecko has only a few hundred exchanges, so page 1 often suffices.","Track total pages from the first response instead of looping blindly.","Expect [] when page exceeds result count — model it as end-of-data."],"tags":["empty-result","pagination","no-data"],"backgroundTag":"empty-result-set","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}