jackwener/OpenCLI · error · CliError

HTTP_ERROR

HTTP_ERROR

Error message

etf failed: HTTP ${resp.status}

What it means

CliError('HTTP_ERROR') thrown in clis/eastmoney/etf.js when the eastmoney push2 ranking API responds with a non-2xx status (resp.ok is false). The library fetches the ranking endpoint with fid/fs=b:MK0021 (场内ETF)/fields/ut params and requires a successful HTTP response before parsing JSON. It signals the remote service rejected or failed the request, not that data was empty (that is NO_DATA).

Source

Thrown at clis/eastmoney/etf.js:49

    const sortKey = String(args.sort ?? 'turnover').toLowerCase();
    const sort = SORTS[sortKey];
    if (!sort) throw new CliError('INVALID_ARGUMENT', `Unknown sort "${sortKey}". Valid: ${Object.keys(SORTS).join(', ')}`);
    const limit = Math.max(1, Math.min(Number(args.limit) || 20, 100));

    const url = new URL('https://push2.eastmoney.com/api/qt/clist/get');
    url.searchParams.set('pn', '1');
    url.searchParams.set('pz', String(limit));
    url.searchParams.set('po', sort.order === 'desc' ? '1' : '0');
    url.searchParams.set('np', '1');
    url.searchParams.set('fltt', '2');
    url.searchParams.set('invt', '2');
    url.searchParams.set('fid', sort.fid);
    url.searchParams.set('fs', 'b:MK0021'); // 场内ETF
    url.searchParams.set('fields', 'f12,f14,f2,f3,f4,f5,f6,f8');
    url.searchParams.set('ut', 'bd1d9ddb04089700cf9c27f6f7426281');

    const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
    if (!resp.ok) throw new CliError('HTTP_ERROR', `etf failed: HTTP ${resp.status}`);
    const data = await resp.json();
    const diff = Array.isArray(data?.data?.diff) ? data.data.diff : [];
    if (diff.length === 0) throw new CliError('NO_DATA', 'eastmoney returned no ETF data');

    return diff.slice(0, limit).map((it, i) => ({
      rank: i + 1,
      code: it.f12,
      name: it.f14,
      price: it.f2,
      changePercent: it.f3,
      change: it.f4,
      turnover: it.f6,
      volume: it.f5,
      turnoverRate: it.f8,
    }));
  },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run after a short delay; many failures are transient 5xx or rate-limit responses.
  2. Read resp.status in the message: 403/429 means IP or header blocking — change network or back off.
  3. Confirm the endpoint and ut token still work by opening the request URL in a browser.
  4. Add retry with exponential backoff for 429/5xx; fail fast on other 4xx.
  5. Update the library/CLI if eastmoney changed the API surface (persistent 404/400).

Example fix

// before
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
if (!resp.ok) throw new CliError('HTTP_ERROR', `etf failed: HTTP ${resp.status}`);
// after
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0', 'Referer': 'https://quote.eastmoney.com/' } });
if (!resp.ok) {
  if (resp.status === 429 || resp.status >= 500) return retryWithBackoff(() => fetchEtf(limit));
  throw new CliError('HTTP_ERROR', `etf failed: HTTP ${resp.status}`);
}
Defensive patterns

Strategy: retry

Validate before calling

const url = new URL('https://push2.eastmoney.com/api/qt/clist/get');
url.searchParams.set('fs', 'b:MK0021');
url.searchParams.set('ut', 'bd1d9ddb04089700cf9c27f6f7426281');
const probe = await fetch(url, { method: 'HEAD' });
if (!probe.ok) throw new Error(`eastmoney unreachable: HTTP ${probe.status}`);

Type guard

function isOkResponse(resp) { return typeof resp === 'object' && resp !== null && 'ok' in resp && resp.ok === true; }

Try / catch

try {
  const etf = await fetchEtf({ limit: 10 });
} catch (err) {
  if (err instanceof CliError && err.code === 'HTTP_ERROR') {
    if (/HTTP (429|5\d\d)/.test(err.message)) return retryWithBackoff(() => fetchEtf({ limit: 10 }), 3);
    console.error(`eastmoney request failed (${err.message}); check network/IP or API status`);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Any fetch to the eastmoney ETF ranking endpoint returning e.g. 403 (UA/WAF block), 429 (rate limited), 5xx (eastmoney outage), or 404 (endpoint changed) — checked via `if (!resp.ok) throw new CliError('HTTP_ERROR', ...)` at clis/eastmoney/etf.js:49.

Common situations: Running from a datacenter IP blocked by eastmoney's WAF (403), hammering the API in a loop and getting throttled (429), transient eastmoney server errors (502/503), or eastmoney deprecating the endpoint or ut token.

Related errors


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