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

  1. Retry later or check CoinGecko rate-limit/pro-plan status; the error body in data usually explains the throttle.
  2. Verify the CoinGecko request URL/ids parameters actually match listed tokens.
  3. Let the script proceed to the built-in CoinPaprika fallback instead of hard-requiring CoinGecko.
  4. 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

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


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)