{"record":{"id":"7b8fb442f28f1664","repo":"jackwener/OpenCLI","slug":"toutiao-hot-board-failed-http-resp-status","errorCode":null,"errorMessage":"toutiao hot-board failed: HTTP ${resp.status}","messagePattern":"toutiao hot-board failed: HTTP (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/toutiao/hot.js","lineNumber":41,"sourceCode":"        { name: 'limit', type: 'int', default: 30, help: '返回条数 (1-50)' },\n    ],\n    columns: ['rank', 'group_id', 'title', 'query', 'hot_value', 'label', 'url', 'image_url'],\n    func: async (kwargs) => {\n        const limit = parseHotLimit(kwargs?.limit, 30);\n        let resp;\n        try {\n            resp = await fetch(HOT_BOARD_URL, {\n                headers: {\n                    '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',\n                    Accept: 'application/json',\n                    Referer: 'https://www.toutiao.com/',\n                },\n            });\n        } catch (error) {\n            throw new CommandExecutionError(`toutiao hot-board request failed: ${error?.message || error}`);\n        }\n        if (!resp.ok) {\n            throw new CommandExecutionError(`toutiao hot-board failed: HTTP ${resp.status}`);\n        }\n        let payload;\n        try {\n            payload = await resp.json();\n        } catch (error) {\n            throw new CommandExecutionError(`toutiao hot-board returned malformed JSON: ${error?.message || error}`);\n        }\n        if (payload?.status && payload.status !== 'success') {\n            throw new CommandExecutionError(`toutiao hot-board returned status=${payload.status}`);\n        }\n        if (payload?.error || payload?.message) {\n            throw new CommandExecutionError(`toutiao hot-board returned error: ${payload.error || payload.message}`);\n        }\n        const list = Array.isArray(payload?.data) ? payload.data : [];\n        const rows = list.map(mapHotRow).filter(Boolean).slice(0, limit);\n        if (rows.length === 0) {\n            throw new EmptyResultError('toutiao hot', '上游 hot-board 返回空列表。');\n        }","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/toutiao/hot.js#L23-L59","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif (!resp.ok) {\n  throw new CommandExecutionError(`toutiao hot-board failed: HTTP ${resp.status}`);\n}\n// after — backoff on retryable statuses\nif (!resp.ok) {\n  if ((resp.status === 429 || resp.status >= 500) && attempt < maxRetries) {\n    await new Promise(r => setTimeout(r, 1000 * 2 ** attempt));\n    return fetchHotBoard(attempt + 1);\n  }\n  throw new CommandExecutionError(`toutiao hot-board failed: HTTP ${resp.status}`);\n}","handlingStrategy":"retry","validationCode":"// pre-check the endpoint returns 2xx before parsing data\nconst pre = await fetch(HOT_BOARD_URL, { headers: { 'User-Agent': UA, 'Referer': 'https://www.toutiao.com/' } });\nif (!pre.ok) throw new Error(`endpoint unhealthy: HTTP ${pre.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  const rows = await toutiaoHot({ limit: 30 });\n} catch (e) {\n  const m = e.message.match(/HTTP (\\d+)/);\n  if (m && (m[1] === '429' || m[1].startsWith('5'))) {\n    await sleep(2000); // backoff then retry once\n    return toutiaoHot({ limit: 30 });\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["http","http-status","rate-limit","anti-bot"],"backgroundTag":"http-429-rate-limited","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}