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

  1. 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.
  2. Add exponential backoff with retries for 429/5xx before surfacing the failure.
  3. Reduce call frequency or cache results between invocations.
  4. 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

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/fc5a81b2e6f83f25. Report an issue: GitHub.