jackwener/OpenCLI · error · CliError

HTTP_ERROR

HTTP_ERROR

Error message

`longhu failed: HTTP ${resp.status}`

What it means

The eastmoney longhu CLI throws CliError('HTTP_ERROR') when the push2.eastmoney.com API responds with a non-OK status (resp.ok is false). It aborts before attempting resp.json() because the body is not trusted to be valid JSON. The thrown message embeds the HTTP status code so the caller can tell whether it is a rate limit (429), bad request (400), or server error (5xx).

Source

Thrown at clis/eastmoney/longhu.js:46

  ],
  columns: ['tradeDate', 'code', 'name', 'closePrice', 'changeRate', 'boardAmt', 'buyAmt', 'sellAmt', 'netAmt', 'turnover', 'dealRatio', 'market', 'reason'],
  func: async (args) => {
    const sinceDate = String(args.date || '').trim() || defaultTradeDate();
    const limit = Math.max(1, Math.min(Number(args.limit) || 20, 100));

    const url = new URL('https://datacenter-web.eastmoney.com/api/data/v1/get');
    url.searchParams.set('sortColumns', 'TRADE_DATE,SECURITY_CODE');
    url.searchParams.set('sortTypes', '-1,1');
    url.searchParams.set('pageSize', String(limit));
    url.searchParams.set('pageNumber', '1');
    url.searchParams.set('reportName', 'RPT_DAILYBILLBOARD_DETAILS');
    url.searchParams.set('columns', 'ALL');
    url.searchParams.set('source', 'WEB');
    url.searchParams.set('client', 'WEB');
    url.searchParams.set('filter', `(TRADE_DATE>='${sinceDate}')`);

    const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
    if (!resp.ok) throw new CliError('HTTP_ERROR', `longhu failed: HTTP ${resp.status}`);
    const data = await resp.json();
    /** @type {any[]} */
    const rows = Array.isArray(data?.result?.data) ? data.result.data : [];
    if (rows.length === 0) throw new CliError('NO_DATA', `No longhu data since ${sinceDate}`);

    return rows.slice(0, limit).map((it) => ({
      tradeDate: String(it.TRADE_DATE || '').slice(0, 10),
      code: it.SECURITY_CODE,
      name: it.SECURITY_NAME_ABBR,
      closePrice: it.CLOSE_PRICE,
      changeRate: it.CHANGE_RATE,
      boardAmt: it.BILLBOARD_DEAL_AMT,
      buyAmt: it.BILLBOARD_BUY_AMT,
      sellAmt: it.BILLBOARD_SELL_AMT,
      netAmt: it.BILLBOARD_NET_AMT,
      turnover: it.ACCUM_AMOUNT,
      dealRatio: it.DEAL_AMOUNT_RATIO,
      market: it.TRADE_MARKET,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check the HTTP status in the message: 403/429 means rate-limited — slow down requests and add delays/backoff between calls.
  2. Verify the sinceDate argument is a valid 'YYYY-MM-DD' string; a malformed date can make the filter expression fail server-side.
  3. Retry after a few minutes if the status is 5xx — eastmoney infrastructure is often transiently unavailable.
  4. Curl the same URL with the same User-Agent header outside the CLI to confirm whether the endpoint itself is reachable.
  5. Check for eastmoney API changes (columns/source/client/fs params) if the failure is persistent across all dates.

Example fix

// before
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
if (!resp.ok) throw new CliError('HTTP_ERROR', `longhu failed: HTTP ${resp.status}`);
// after — retry with backoff before giving up
let resp;
for (let attempt = 0; attempt < 3; attempt++) {
  resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
  if (resp.ok) break;
  if (resp.status < 500 && resp.status !== 429) break; // non-retryable
  await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
}
if (!resp.ok) throw new CliError('HTTP_ERROR', `longhu failed: HTTP ${resp.status}`);
Defensive patterns

Strategy: try-catch

Validate before calling

// Non-blocking pre-check: verify the endpoint is reachable and the CLI is not rate-limited
const res = await fetch('https://push2.eastmoney.com', { headers: { 'User-Agent': 'Mozilla/5.0' } });
if (res.status === 403 || res.status === 429) console.warn('eastmoney is throttling this client; expect HTTP_ERROR');

Type guard

function isCliError(e) { return e instanceof Error && 'code' in e; }
function isHttpError(e) { return isCliError(e) && e.code === 'HTTP_ERROR'; }

Try / catch

try {
  const rows = await getLonghu({ since, limit });
} catch (err) {
  if (err instanceof CliError && err.code === 'HTTP_ERROR') {
    if (/HTTP (429|403)/.test(err.message)) {
      await sleep(5000); return getLonghu({ since, limit }); // backoff and retry once
    }
    console.error(`eastmoney unreachable (${err.message}); try again later`);
    process.exitCode = 69; // EX_UNAVAILABLE
  } else throw err;
}

Prevention

When it happens

Trigger: Any fetch to the eastmoney longhu (Dragon-Tiger list) endpoint whose response status is outside 200-299: eastmoney WAF/rate-limit pages returning 403/429, endpoint schema changes returning 400 for the filter `(TRADE_DATE>='${sinceDate}')`, or transient 5xx outages of push2.eastmoney.com.

Common situations: Hitting the public eastmoney API too frequently from scripts or CI (rate limiting), an invalid/malformed sinceDate making the filter expression rejected, corporate proxies blocking the request, or eastmoney deprecating/changing the WEB endpoint so the server rejects the parameter combination.

Related errors


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