{"record":{"id":"2d3b14c57993276f","repo":"jackwener/OpenCLI","slug":"toutiao-hot-board-request-failed-error-message","errorCode":null,"errorMessage":"toutiao hot-board request failed: ${error?.message || error}","messagePattern":"toutiao hot-board request failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/toutiao/hot.js","lineNumber":38,"sourceCode":"    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { 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);","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/toutiao/hot.js#L20-L56","documentation":"The toutiao `hot` command fetches Toutiao's public hot-board JSON (hot-event/hot-board) via fetch(). This error wraps any exception thrown by fetch itself — DNS failure, connection refused/reset, TLS error, timeout/abort, or invalid URL — re-thrown as a CommandExecutionError with the underlying cause message appended. It means the HTTP request never completed, so no response object exists.","triggerScenarios":"Calling the `toutiao hot` command when the network to www.toutiao.com is unreachable (offline, DNS failure, firewall/GFW blocking), the request is aborted (timeout/AbortSignal), or HOT_BOARD_URL becomes invalid — fetch() rejects before resp.ok can be checked.","commonSituations":"Running the CLI offline or behind a proxy that refuses connections; corporate/GFW blocking of toutiao.com; Node <18 where global fetch is undefined (TypeError 'fetch is not a function'); transient connection resets on mobile/VPN networks; typo'd or redirected HOT_BOARD_URL in utils.js.","solutions":["Check basic network connectivity to the endpoint (curl -I with the same User-Agent/Referer headers) to confirm reachability.","If 'fetch is not a function' appears, upgrade to Node 18+ or add a fetch polyfill (undici/node-fetch).","Configure or disable proxy env vars (HTTPS_PROXY/NO_PROXY) so the request can route out; retry after transient failures.","If toutiao.com is blocked from your region, run from a network that can reach it or supply an alternative mirror URL."],"exampleFix":"// before\nresp = await fetch(HOT_BOARD_URL, { headers });\n// after — retry transient network failures\nlet resp;\nfor (let attempt = 0; attempt < 3; attempt++) {\n  try {\n    resp = await fetch(HOT_BOARD_URL, { headers });\n    break;\n  } catch (e) {\n    if (attempt === 2) throw new CommandExecutionError(`toutiao hot-board request failed: ${e?.message || e}`);\n    await new Promise(r => setTimeout(r, 500 * (attempt + 1)));\n  }\n}","handlingStrategy":"retry","validationCode":"// verify the endpoint is reachable before invoking the command\ncurl -sS -o /dev/null -w '%{http_code}' \\\n  -H '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  -H 'Referer: https://www.toutiao.com/' \\\n  https://www.toutiao.com/hot-event/hot-board/\n// also: node -e \"console.log(typeof fetch)\" # must print 'function' (Node 18+)","typeGuard":null,"tryCatchPattern":"try {\n  const rows = await toutiaoHot({ limit: 30 });\n} catch (e) {\n  if (/request failed/.test(e.message)) {\n    // network-layer failure: check connectivity/proxy, retry with backoff\n    await sleep(1000);\n    return toutiaoHot({ limit: 30 });\n  }\n  throw e;\n}","preventionTips":["Require Node 18+ so global fetch exists.","Set HTTPS_PROXY/NO_PROXY correctly in environments behind proxies.","Wrap scheduled scrapes with retry + exponential backoff for transient network errors.","Pre-flight check connectivity to toutiao.com in CI or cron scripts before running."],"tags":["network","fetch","http-request","cli"],"backgroundTag":"fetch-network-error","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}