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
- Check the HTTP status in the message: 403/429 means rate-limited — slow down requests and add delays/backoff between calls.
- Verify the sinceDate argument is a valid 'YYYY-MM-DD' string; a malformed date can make the filter expression fail server-side.
- Retry after a few minutes if the status is 5xx — eastmoney infrastructure is often transiently unavailable.
- Curl the same URL with the same User-Agent header outside the CLI to confirm whether the endpoint itself is reachable.
- 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
- Throttle requests to eastmoney and add exponential backoff for 429/5xx responses.
- Always send a desktop User-Agent header on every call.
- Validate date arguments ('YYYY-MM-DD') before building the filter query.
- Monitor eastmoney API changes; pin and test the endpoint URL in a smoke test.
- Catch CliError by its code property rather than parsing message text.
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.