jackwener/OpenCLI · error · CommandExecutionError

coingecko returned an unexpected response

Error message

coingecko returned an unexpected response

What it means

This CommandExecutionError is thrown when the response parses as JSON, has no error field, but is not an array — /coins/markets must return a JSON array of coin objects. The library guards against unexpected shapes (object, string, null) before mapping columns. It indicates a schema mismatch or interception by something returning its own JSON.

Source

Thrown at clis/coingecko/top.js:49

    url.searchParams.set('per_page', String(limit));
    url.searchParams.set('page', '1');
    url.searchParams.set('sparkline', 'false');

    let resp;
    try {
      resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
    } catch (error) {
      throw new CommandExecutionError(`coingecko top request failed: ${error?.message || error}`);
    }
    if (!resp.ok) throw new CommandExecutionError(`coingecko top failed: HTTP ${resp.status}`);
    let data;
    try {
      data = await resp.json();
    } catch (error) {
      throw new CommandExecutionError(`coingecko returned malformed JSON: ${error?.message || error}`);
    }
    if (data?.error) throw new CommandExecutionError(`coingecko returned error: ${data.error}`);
    if (!Array.isArray(data)) throw new CommandExecutionError('coingecko returned an unexpected response');
    if (data.length === 0) throw new EmptyResultError('coingecko top', 'coingecko returned no market data');

    return data.map((c) => ({
      rank: c.market_cap_rank,
      symbol: String(c.symbol ?? '').toUpperCase(),
      name: c.name,
      price: c.current_price,
      change24hPct: c.price_change_percentage_24h,
      marketCap: c.market_cap,
      volume24h: c.total_volume,
      high24h: c.high_24h,
      low24h: c.low_24h,
    }));
  },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. curl the endpoint and compare the actual JSON shape with the expected array of coin objects.
  2. Confirm you are hitting api.coingecko.com/api/v3/coins/markets and not a redirected/intercepted URL.
  3. Check the CoinGecko changelog for /coins/markets schema changes and update the library.
  4. Bypass proxies/VPN/ad-blockers that could substitute their own JSON responses.
  5. Retry later if it is a transient CDN incident.

Example fix

// caller: defensive shape check on results
// before
const rows = await runCli('coingecko', 'top');
console.log(rows[0].price);
// after
const rows = await runCli('coingecko', 'top');
if (!Array.isArray(rows) || rows.length === 0) throw new Error('unexpected coingecko output');
console.log(rows[0].price);
Defensive patterns

Strategy: type-guard

Validate before calling

const probe = await fetch('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&per_page=1');
const probeBody = await probe.json();
if (!Array.isArray(probeBody)) throw new Error('unexpected /coins/markets shape: ' + JSON.stringify(probeBody).slice(0, 200));

Type guard

function isCoinArray(d) { return Array.isArray(d) && d.every(c => c && typeof c === 'object' && 'id' in c && 'current_price' in c); }

Try / catch

try {
  rows = await runCli('coingecko', 'top');
} catch (e) {
  if (/unexpected response/.test(e.message)) {
    console.error('Response shape drifted — check CoinGecko changelog / intercepting proxies');
  }
  throw e;
}

Prevention

When it happens

Trigger: API returning {data:[...]} after a schema change; a proxy or captive portal returning its own JSON error object; CoinGecko returning a JSON status object on soft failures; accidentally hitting a different endpoint/version.

Common situations: Major API version bumps altering the response envelope; middleboxes (proxies, ad-blockers, security appliances) substituting JSON; CDN error JSON with 200 status; mocking layers returning wrong shapes in tests.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/25dcb695bbeff811. Report an issue: GitHub.