jackwener/OpenCLI · error · CommandExecutionError
toutiao hot-board failed: HTTP ${resp.status}
Error message
toutiao hot-board failed: HTTP ${resp.status} What it means
After the fetch resolves, the command checks resp.ok; any non-2xx status from the Toutiao hot-board endpoint throws this error with the HTTP status code. It means the request completed but the server rejected or failed it (rate limit, anti-bot block, 5xx).
Source
Thrown at clis/toutiao/hot.js:41
{ name: 'limit', type: 'int', default: 30, help: '返回条数 (1-50)' },
],
columns: ['rank', 'group_id', 'title', 'query', 'hot_value', 'label', 'url', 'image_url'],
func: async (kwargs) => {
const limit = parseHotLimit(kwargs?.limit, 30);
let resp;
try {
resp = await fetch(HOT_BOARD_URL, {
headers: {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
Accept: 'application/json',
Referer: 'https://www.toutiao.com/',
},
});
} catch (error) {
throw new CommandExecutionError(`toutiao hot-board request failed: ${error?.message || error}`);
}
if (!resp.ok) {
throw new CommandExecutionError(`toutiao hot-board failed: HTTP ${resp.status}`);
}
let payload;
try {
payload = await resp.json();
} catch (error) {
throw new CommandExecutionError(`toutiao hot-board returned malformed JSON: ${error?.message || error}`);
}
if (payload?.status && payload.status !== 'success') {
throw new CommandExecutionError(`toutiao hot-board returned status=${payload.status}`);
}
if (payload?.error || payload?.message) {
throw new CommandExecutionError(`toutiao hot-board returned error: ${payload.error || payload.message}`);
}
const list = Array.isArray(payload?.data) ? payload.data : [];
const rows = list.map(mapHotRow).filter(Boolean).slice(0, limit);
if (rows.length === 0) {
throw new EmptyResultError('toutiao hot', '上游 hot-board 返回空列表。');
}View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the status: for 403/429, slow down requests (add delays/backoff) and send browser-like User-Agent, Accept, and Referer headers.
- Retry with exponential backoff for transient 429/5xx responses.
- Verify HOT_BOARD_URL in clis/toutiao/utils.js still matches the live endpoint; update it if Toutiao moved the API (check the homepage hot panel's network request).
- Try from a residential network/IP if a datacenter IP is being blocked.
Example fix
// before
if (!resp.ok) {
throw new CommandExecutionError(`toutiao hot-board failed: HTTP ${resp.status}`);
}
// after — backoff on retryable statuses
if (!resp.ok) {
if ((resp.status === 429 || resp.status >= 500) && attempt < maxRetries) {
await new Promise(r => setTimeout(r, 1000 * 2 ** attempt));
return fetchHotBoard(attempt + 1);
}
throw new CommandExecutionError(`toutiao hot-board failed: HTTP ${resp.status}`);
} Defensive patterns
Strategy: retry
Validate before calling
// pre-check the endpoint returns 2xx before parsing data
const pre = await fetch(HOT_BOARD_URL, { headers: { 'User-Agent': UA, 'Referer': 'https://www.toutiao.com/' } });
if (!pre.ok) throw new Error(`endpoint unhealthy: HTTP ${pre.status}`); Try / catch
try {
const rows = await toutiaoHot({ limit: 30 });
} catch (e) {
const m = e.message.match(/HTTP (\d+)/);
if (m && (m[1] === '429' || m[1].startsWith('5'))) {
await sleep(2000); // backoff then retry once
return toutiaoHot({ limit: 30 });
}
throw e;
} Prevention
- Throttle request frequency; add jittered delays between calls to avoid 429.
- Always send the browser-like User-Agent/Accept/Referer headers.
- Pin/monitor HOT_BOARD_URL; alert on sustained 404/403 so you can update the endpoint.
- For 403 from datacenter IPs, run from a residential network or approved egress.
When it happens
Trigger: Calling `toutiao hot` and receiving HTTP 403/429 (anti-scraping/rate limiting from missing or flagged headers), 5xx from the hot-board service, or 404 if the endpoint path moved.
Common situations: Hammering the endpoint in a loop triggers 429 rate limiting; Toutiao's WAF returns 403 to datacenter IPs or unusual User-Agents; the hot-board URL changes during frontend deployments causing 404; upstream incidents return 5xx.
Related errors
- ${label} returned HTTP ${outcome.status}
- toutiao recommend failed: HTTP ${resp.status}
- WeRead search page request failed: HTTP ${resp.status}
- FETCH_ERROR
- Xiaoyuzhou transcript download failed with HTTP ${response.s
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/7b8fb442f28f1664.
Report an issue: GitHub.