{"record":{"id":"ae3c8403b3ec81fc","repo":"jackwener/OpenCLI","slug":"toutiao-hot-board-returned-error-payload-error","errorCode":null,"errorMessage":"toutiao hot-board returned error: ${payload.error || payload.message}","messagePattern":"toutiao hot-board returned error: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/toutiao/hot.js","lineNumber":53,"sourceCode":"                },\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        }\n        // Re-rank (1..N) after filter so ranks are dense even if upstream had nulls.\n        return rows.map((row, idx) => ({ ...row, rank: idx + 1 }));\n    },\n});\n","sourceCodeStart":35,"sourceCodeEnd":64,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/toutiao/hot.js#L35-L64","documentation":"If the parsed hot-board JSON contains an `error` or `message` field, the command throws this CommandExecutionError carrying that upstream message. It means the API signalled a specific failure (rate limit notice, permission denial, endpoint deprecation) inside an otherwise successful-looking JSON envelope.","triggerScenarios":"Calling `toutiao hot` when the endpoint responds with JSON containing error/message fields such as {message:'请求过于频繁'} (rate limit), {error:'forbidden'}, or a deprecation/parameter notice.","commonSituations":"Toutiao's anti-scraping layer returns a JSON notice instead of data after too many requests; API version deprecated with a message telling callers to migrate; regional/IP restrictions returned as a JSON message.","solutions":["Read the embedded error/message text — it names the upstream cause (rate limit, forbidden, deprecated).","If rate-limited, back off and slow request frequency; reuse one session rather than hammering.","If the message indicates the endpoint moved/deprecated, locate the new hot-board URL (from the toutiao.com homepage network panel) and update HOT_BOARD_URL in clis/toutiao/utils.js.","Retry later if the message signals temporary unavailability."],"exampleFix":"// before\nif (payload?.error || payload?.message) {\n  throw new CommandExecutionError(`toutiao hot-board returned error: ${payload.error || payload.message}`);\n}\n// after — some payloads use message for non-error notices\nif (payload?.error || (payload?.message && payload.status !== 'success')) {\n  throw new CommandExecutionError(`toutiao hot-board returned error: ${payload.error || payload.message}`);\n}","handlingStrategy":"try-catch","validationCode":"// treat any error/message envelope as a failure signal before mapping rows\nconst hasUpstreamError = (p) => p && typeof p === 'object' && Boolean(p.error || (p.message && p.status !== 'success'));","typeGuard":"function hasUpstreamError(p) {\n  return p !== null && typeof p === 'object' &&\n    (typeof p.error === 'string' && p.error.length > 0 ||\n     typeof p.message === 'string' && p.message.length > 0 && p.status !== 'success');\n}","tryCatchPattern":"try {\n  const rows = await toutiaoHot({ limit: 30 });\n} catch (e) {\n  if (/returned error:/.test(e.message)) {\n    // upstream reported a specific problem (rate limit/deprecated) — read the\n    // message after the colon and adjust frequency or endpoint accordingly\n    console.error(e.message);\n    return fallbackHotSource();\n  }\n  throw e;\n}","preventionTips":["Keep request rates low so the upstream never emits rate-limit messages.","Log the upstream error text — it usually says exactly what to change.","Maintain a fallback data source for the hot list when Toutiao signals denial.","Subscribe to/monitor the endpoint so deprecation messages are caught before breaking users."],"tags":["api","upstream","rate-limit","response-contract"],"backgroundTag":"upstream-api-error-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}