jackwener/OpenCLI · error · CommandExecutionError
toutiao recommend failed: HTTP ${resp.status}
Error message
toutiao recommend failed: HTTP ${resp.status} What it means
This CommandExecutionError is thrown when the upstream Toutiao recommend endpoint responds with a non-2xx HTTP status. The status code is embedded in the message so developers can immediately distinguish auth issues (403), rate limiting (429), and server errors (5xx).
Source
Thrown at clis/toutiao/recommend.js:52
columns: ['rank', 'group_id', 'title', 'abstract', 'source', 'tag', 'comments', 'published_at', 'url', 'image_url'],
func: async (kwargs) => {
const category = parseRecommendCategory(kwargs?.category, '__all__');
const limit = parseRecommendLimit(kwargs?.limit, 20);
const url = `${RECOMMEND_URL}?category=${encodeURIComponent(category)}`;
let resp;
try {
resp = await fetch(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 recommend request failed: ${error?.message || error}`);
}
if (!resp.ok) {
throw new CommandExecutionError(`toutiao recommend failed: HTTP ${resp.status}`);
}
let payload;
try {
payload = await resp.json();
} catch (error) {
throw new CommandExecutionError(`toutiao recommend returned malformed JSON: ${error?.message || error}`);
}
if (payload?.message && payload.message !== 'success') {
throw new CommandExecutionError(`toutiao recommend returned message=${payload.message}`);
}
if (!Array.isArray(payload?.data)) {
throw new CommandExecutionError('toutiao recommend returned a non-array data field');
}
const rows = payload.data.map(mapRecommendRow).filter(Boolean).slice(0, limit);
if (rows.length === 0) {
throw new EmptyResultError('toutiao recommend', `频道 ${category} 返回空列表。`);
}
// Re-rank (1..N) after filter so ranks are dense even if upstream had ads.View on GitHub (pinned to 49907e53dc)
Solutions
- Read the HTTP status from the message and act accordingly: 403 → vary IP/User-Agent or add valid cookies; 429 → back off and slow request rate; 5xx → retry later.
- Add exponential backoff with retries for 429/5xx before surfacing the failure.
- Reduce call frequency or cache results between invocations.
- Verify the endpoint URL is still valid — upstream API paths occasionally change and start returning 404.
Example fix
// before
await recommend({ category: 'tech' });
// after
try {
await recommend({ category: 'tech' });
} catch (e) {
const m = /HTTP (\d+)/.exec(e.message);
if (m && (m[1] === '429' || m[1].startsWith('5'))) {
await new Promise(r => setTimeout(r, 5000));
return recommend({ category: 'tech' });
}
throw e;
} Defensive patterns
Strategy: retry
Try / catch
try {
return await recommend({ category });
} catch (e) {
const m = /HTTP (\d{3})/.exec(e.message);
const status = m && Number(m[1]);
if (status === 429 || (status >= 500 && status < 600)) {
await new Promise(r => setTimeout(r, 5000));
return recommend({ category }); // retry with backoff
}
if (status === 403) throw new Error('Blocked (403): rotate IP/UA or add cookies');
throw e;
} Prevention
- Throttle request rate and cache results to avoid 429s.
- For 403s, send realistic headers/cookies and avoid datacenter IPs.
- Retry only retryable statuses (429, 5xx), not 4xx client errors.
- Alert on persistent 5xx — it is an upstream outage.
When it happens
Trigger: Calling 'toutiao recommend' when resp.ok is false — e.g. upstream returns 403 (anti-bot block), 429 (rate limit), 500/502/503 (server-side failure).
Common situations: Toutiao's anti-scraping measures returning 403 for datacenter IPs or missing cookies; hitting the endpoint too frequently causing 429; temporary upstream outages producing 5xx.
Related errors
- ${label} returned HTTP ${outcome.status}
- toutiao hot-board 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/fc5a81b2e6f83f25.
Report an issue: GitHub.