jackwener/OpenCLI · error · CliError
HTTP_ERROR
HTTP_ERROR
Error message
announcement failed: HTTP ${resp.status} What it means
The announcement fetch to Eastmoney's web API returned a non-2xx HTTP status. The code checks resp.ok after the fetch and wraps the status into a CliError with code HTTP_ERROR. This is a server/endpoint-level failure, not a parsing problem.
Source
Thrown at clis/eastmoney/announcement.js:35
args: [
{ name: 'market', type: 'string', default: 'SHA,SZA,BJA', help: '交易所:SHA (沪) / SZA (深) / BJA (北) 可逗号分隔' },
{ name: 'limit', type: 'int', default: 20, help: '返回数量 (max 100)' },
],
columns: ['time', 'code', 'name', 'title', 'category', 'url'],
func: async (args) => {
const market = String(args.market ?? 'SHA,SZA,BJA').trim() || 'SHA,SZA,BJA';
const limit = Math.max(1, Math.min(Number(args.limit) || 20, 100));
const url = new URL('https://np-anotice-stock.eastmoney.com/api/security/ann');
url.searchParams.set('page_size', String(limit));
url.searchParams.set('page_index', '1');
url.searchParams.set('ann_type', market);
url.searchParams.set('client_source', 'web');
url.searchParams.set('f_node', '0');
url.searchParams.set('s_node', '0');
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
if (!resp.ok) throw new CliError('HTTP_ERROR', `announcement failed: HTTP ${resp.status}`);
const data = await resp.json();
const list = Array.isArray(data?.data?.list) ? data.data.list : [];
if (list.length === 0) throw new CliError('NO_DATA', 'eastmoney returned no announcement data');
return list.slice(0, limit).map((it) => {
const primary = Array.isArray(it.codes) && it.codes.length > 0 ? it.codes[0] : {};
const cat = Array.isArray(it.columns) && it.columns.length > 0 ? it.columns[0]?.column_name : '';
return {
time: String(it.notice_date || it.display_time || '').slice(0, 19),
code: primary.stock_code || '',
name: primary.short_name || '',
title: it.title || it.title_ch || '',
category: cat || '',
url: `https://data.eastmoney.com/notices/detail/${primary.stock_code || ''}/${it.art_code || ''}.html`,
};
});
},
});View on GitHub (pinned to 49907e53dc)
Solutions
- Log resp.status, fix the obvious cause (429 → back off and slow down; 403 → change IP/headers; 5xx → retry later).
- Retry with exponential backoff for transient 5xx/429.
- Refresh the User-Agent/headers to look like a real browser if blocked.
- Verify the endpoint URL and query params are still valid against the current Eastmoney web API.
Example fix
// before
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
if (!resp.ok) throw new CliError('HTTP_ERROR', `announcement failed: HTTP ${resp.status}`);
// after
for (let i = 0; i < 3; i++) {
const resp = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
if (resp.ok) return handle(await resp.json());
if (resp.status === 429 || resp.status >= 500) await new Promise(r => setTimeout(r, 2 ** i * 1000));
else throw new CliError('HTTP_ERROR', `announcement failed: HTTP ${resp.status}`);
} Defensive patterns
Strategy: retry
Validate before calling
// no pre-call validation possible for remote HTTP status; ensure URL/params are well-formed:
const u = new URL('https://np-anotice-stock.eastmoney.com/api/security/ann');
// verify url.toString() and params before fetching Type guard
null
Try / catch
try {
rows = await getAnnouncements(secid);
} catch (e) {
if (e?.code === 'HTTP_ERROR' && /HTTP (429|5\d\d)/.test(e.message)) {
await backoff(); rows = await getAnnouncements(secid); // retry transient failures
} else if (e?.code === 'HTTP_ERROR') {
console.error(`Eastmoney rejected the request (${e.message}); check endpoint/params/headers`);
} else throw e;
} Prevention
- Retry 429/5xx with exponential backoff and jitter
- Rate-limit calls to avoid anti-scraping blocks
- Keep the User-Agent and headers browser-like and current
- Monitor for Eastmoney endpoint changes
When it happens
Trigger: Eastmoney returns 4xx/5xx for the announcement endpoint (rate limiting, temporary outage, WAF/anti-bot block, invalid query params causing 400). Any fetch where !resp.ok triggers this.
Common situations: Hammering the endpoint in a loop triggers rate limiting or an anti-scraping block; Eastmoney changes/retires the endpoint; a corporate proxy returns 403/502; transient 5xx during market-hours load.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/ad4cc7fe5b715ece.
Report an issue: GitHub.