koala73/worldmonitor · error
CoinGecko returned no data
Error message
CoinGecko returned no data
What it means
fetchFromCoinGecko in the token-panel seeding script validates that the CoinGecko markets endpoint returned a non-empty JSON array. If the parsed body is not an array or has zero items, the script throws so the CoinPaprika fallback can be attempted. It guards against silently seeding empty panel data when CoinGecko returns an empty or differently-shaped response.
Solutions
- Retry later or check CoinGecko rate-limit/pro-plan status; the error body in data usually explains the throttle.
- Verify the CoinGecko request URL/ids parameters actually match listed tokens.
- Let the script proceed to the built-in CoinPaprika fallback instead of hard-requiring CoinGecko.
- Cache last good seed data so transient empty responses don't wipe panels.
Example fix
// before
const data = await resp.json();
if (!Array.isArray(data) || data.length === 0) throw new Error('CoinGecko returned no data');
// after
const data = await resp.json().catch(() => null);
if (!Array.isArray(data) || data.length === 0) {
console.warn('CoinGecko returned no usable data; using fallback');
return fetchFromCoinPaprika();
} Defensive patterns
Strategy: fallback
Validate before calling
const probe = await resp.json().catch(() => null); if (!Array.isArray(probe) || probe.length === 0) return fetchFromCoinPaprika();
Type guard
function isTickerArray(v) { return Array.isArray(v) && v.every(t => t && typeof t.id === 'string'); } Try / catch
try { return await fetchFromCoinGecko(); } catch (err) { console.warn('CoinGecko unavailable:', err.message); return await fetchFromCoinPaprika(); } Prevention
- Respect CoinGecko free-tier rate limits with caching and staggering.
- Log the raw response body when shape validation fails to diagnose throttles.
- Always keep a working secondary data source wired in.
When it happens
Trigger: The retry-budgeted GET to CoinGecko succeeded with HTTP 200 but the JSON body parsed to something other than an array (e.g. {"status":{"error_code":...}} rate-limit/pro limit payload) or an empty array (no tokens matched the query).
Common situations: CoinGecko free-tier rate limiting returning 200-with-error-body, a bad/unknown query yielding zero markets rows, CoinGecko API shape changes, or an outage returning an empty result set.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- JODI Gas catalog fetch failed: HTTP
- bodyTransportFailure ? 'NHC_POINT_REQUEST_FAILED' : 'NHC_POINT_RESPONSE_INVALID'
- Brief service unavailable
- callbackUrl DNS resolution failed
- callbackUrl DNS resolution returned no addresses
AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-09-22).
Data as JSON: /api/errors/29f5a99cea570512.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seed-token-panels.mjs:45
export const COINGECKO_RETRY_BUDGET_MS = 45_000;
// The fallback runs COINPAPRIKA_IDS in rounds of COINPAPRIKA_CONCURRENCY, each
// round bounded by one request timeout. Both values are passed to the helper
// explicitly so this derivation describes the call as made, and mapping a new
// token widens the budget instead of silently breaking the invariant.
const COINPAPRIKA_CONCURRENCY = 4;
export const COINPAPRIKA_WORST_CASE_MS = Math.ceil(COINPAPRIKA_IDS.length / COINPAPRIKA_CONCURRENCY) * REQUEST_TIMEOUT_MS;
async function fetchFromCoinGecko() {
const { baseUrl, headers } = coingeckoEndpoint();
const url = `${baseUrl}/coins/markets?vs_currency=usd&ids=${ALL_IDS.join(',')}&order=market_cap_desc&sparkline=false&price_change_percentage=24h,7d`;
const resp = await fetchCoinGeckoWithRetryBudget(url, {
headers,
requestTimeoutMs: REQUEST_TIMEOUT_MS,
budgetMs: COINGECKO_RETRY_BUDGET_MS,
});
const data = await resp.json();
if (!Array.isArray(data) || data.length === 0) throw new Error('CoinGecko returned no data');
return data;
}
async function fetchFromCoinPaprika() {
console.log(' [CoinPaprika] Falling back to CoinPaprika...');
const tickers = await fetchCoinPaprikaTickersById(COINPAPRIKA_IDS, {
concurrency: COINPAPRIKA_CONCURRENCY,
timeoutMs: REQUEST_TIMEOUT_MS,
});
const reverseMap = new Map(Object.entries(COINPAPRIKA_ID_MAP).map(([g, p]) => [p, g]));
return tickers
.map((t) => ({
id: reverseMap.get(t.id) || t.id,
current_price: t.quotes.USD.price,
price_change_percentage_24h: t.quotes.USD.percent_change_24h,
price_change_percentage_7d_in_currency: t.quotes.USD.percent_change_7d,
symbol: t.symbol.toLowerCase(),
name: t.name,View on GitHub (pinned to e586b8b4b8)