jackwener/OpenCLI · error · CliError
HTTP_ERROR
HTTP_ERROR
Error message
`eastmoney quote failed: HTTP ${resp.status}` What it means
After building the ulist.np URL with the resolved secids, quote.js checks resp.ok and throws CliError HTTP_ERROR `eastmoney quote failed: HTTP ${status}` on any non-2xx response from push2.eastmoney.com. It surfaces the upstream status so the caller can distinguish auth/bot-blocking (403), bad parameters (4xx), and server faults (5xx).
Source
Thrown at clis/eastmoney/quote.js:82
throw new CliError('INVALID_ARGUMENT', 'At least one symbol is required');
}
/** @type {string[]} */
const secids = [];
for (const s of raw) {
try { secids.push(resolveSecid(s)); }
catch (err) { throw new CliError('INVALID_ARGUMENT', `Unrecognized symbol "${s}"`); }
}
// Multi-stock in one call via ulist.np
const url = new URL('https://push2.eastmoney.com/api/qt/ulist.np/get');
url.searchParams.set('secids', secids.join(','));
url.searchParams.set('fltt', '2');
url.searchParams.set('fields', FIELDS);
url.searchParams.set('ut', 'bd1d9ddb04089700cf9c27f6f7426281');
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' } });
if (!resp.ok) throw new CliError('HTTP_ERROR', `eastmoney quote 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 quotes', `Check symbols: ${raw.join(', ')}`);
return diff.map((it) => ({
code: it.f12,
name: it.f14,
market: marketLabel(it.f13),
price: it.f2,
changePercent: it.f3,
change: it.f4,
open: it.f17,
high: it.f15,
low: it.f16,
prevClose: it.f18,
volume: it.f5,
turnover: it.f6,
turnoverRate: it.f8,View on GitHub (pinned to 49907e53dc)
Solutions
- Wait and retry — many 403/5xx are transient or rate-limit related
- Read the status code: 403 → back off and reduce request rate; 5xx → eastmoney-side
- Add exponential backoff/retry with jitter around fetch
- Verify direct access: curl the same URL and compare status
- If 403 persists, rotate IP/User-Agent or check for eastmoney API changes (path, fields, ut token)
Example fix
// before
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' } });
if (!resp.ok) throw new CliError('HTTP_ERROR', `eastmoney quote failed: HTTP ${resp.status}`);
// after
const resp = await fetchWithBackoff(url, { headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' } });
if (!resp.ok) throw new CliError('HTTP_ERROR', `eastmoney quote failed: HTTP ${resp.status}`, `Status ${resp.status}; retry later or reduce polling rate`); Defensive patterns
Strategy: retry
Validate before calling
// Preflight: cheap single-symbol probe before bulk calls
const probe = await fetch('https://push2.eastmoney.com/api/qt/ulist.np/get?secids=1.600000&fltt=2&fields=f12&ut=bd1d9ddb04089700cf9c27f6f7426281', { headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' } });
if (!probe.ok) throw new Error(`eastmoney API unreachable: HTTP ${probe.status}`); Type guard
null
Try / catch
try {
await runQuote(args);
} catch (e) {
if (e.code === 'HTTP_ERROR') {
const status = Number(e.message.match(/HTTP (\d+)/)?.[1]);
if (status === 429 || status >= 500) { await sleep(backoff()); return runQuote(args); }
if (status === 403) console.error('Blocked by eastmoney — reduce polling rate or change network/UA.');
} else throw e;
} Prevention
- Throttle quote polling (e.g. ≥1 request/second, backoff on 403/429)
- Use a realistic browser User-Agent and stable egress IP
- Alert on persistent 403/404 — signals anti-bot escalation or API/token change
- Add retries with exponential backoff + jitter for 5xx
- Cache quotes briefly instead of re-fetching per display refresh
When it happens
Trigger: Any non-2xx from the quote endpoint: 403 from eastmoney anti-scraping/rate limiting (very common when polling frequently or from datacenter IPs), 400 if the secids parameter is malformed, 5xx during eastmoney outages or heavy load.
Common situations: High-frequency quote polling triggering rate limits; corporate proxies or firewalls intercepting the request; eastmoney rotating or invalidating the hardcoded ut token; temporary eastmoney server errors during volatile market sessions.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- HTTP_ERROR
- 12306 queryByTrainNo returned HTTP ${resp.status}
- 12306 queryByTrainNo returned non-JSON body
- HTTP_ERROR
- eastmoney convertible failed: HTTP ${resp.status}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/8e9aaa8a84e6a2b9.
Report an issue: GitHub.