{"record":{"id":"59e09a0693a97d89","repo":"jackwener/OpenCLI","slug":"hot-board","errorCode":null,"errorMessage":"上游 hot-board 返回空列表。","messagePattern":"上游 hot-board 返回空列表。","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/toutiao/hot.js","lineNumber":58,"sourceCode":"        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":40,"sourceCodeEnd":64,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/toutiao/hot.js#L40-L64","documentation":"This EmptyResultError is thrown by the toutiao hot-board command when the upstream Toutiao API responds successfully but yields no usable rows after mapping and filtering. The library treats a successful response with an empty list as a distinct, expected outcome rather than silently returning []. It signals that the upstream data source had nothing to serve for the hot board at this moment.","triggerScenarios":"Calling the 'toutiao hot' command when payload.data is an empty array, or when all entries are dropped by mapHotRow().filter(Boolean) (e.g. rows missing required fields), leaving rows.length === 0.","commonSituations":"Upstream temporarily has no hot-board data (off-peak, API deprioritizing the endpoint); upstream changed its JSON shape so mapHotRow now returns null for every row; aggressive filtering removes all rows; regional restrictions returning an empty list.","solutions":["Retry the command later — an empty hot board is often transient on the upstream side.","Log the raw upstream payload once to verify payload.data actually contains rows; if it does not, the upstream schema may have changed and mapHotRow must be updated.","Loosen any mapHotRow field requirements only if the upstream contract genuinely changed.","Handle the empty case in calling code with a friendly 'no data right now' message instead of surfacing it as a hard failure."],"exampleFix":"// before\nconst rows = await hotBoard();\nrender(rows);\n// after\nlet rows;\ntry { rows = await hotBoard(); }\ncatch (e) {\n  if (e instanceof EmptyResultError) rows = [];\n  else throw e;\n}\nrender(rows.length ? rows : [{ title: '暂无热榜数据' }]);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const rows = await hotBoard();\n} catch (e) {\n  if (e instanceof EmptyResultError) {\n    return []; // benign empty state\n  }\n  throw e;\n}","preventionTips":["Treat EmptyResultError as an expected empty state, not a crash.","Retry once after a short delay before giving up — emptiness is often transient.","If empties persist, log the raw upstream payload to detect schema drift breaking mapHotRow."],"tags":["empty-result","upstream-api","cli","no-data"],"backgroundTag":"upstream-empty-result","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}