{"record":{"id":"1b73443a8a01c03f","repo":"jackwener/OpenCLI","slug":"fetch-error-1b7344","errorCode":"FETCH_ERROR","errorMessage":"Sina Finance API HTTP ${res.status}","messagePattern":"Sina Finance API HTTP (.+?)","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/sinafinance/news.js","lineNumber":48,"sourceCode":"    domain: 'app.cj.sina.com.cn',\n    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { name: 'limit', type: 'int', default: 20, help: 'Max results (max 50)' },\n        { name: 'type', type: 'int', default: 0, help: 'News type: 0=全部 1=A股 2=宏观 3=公司 4=数据 5=市场 6=国际 7=观点 8=央行 9=其它' },\n    ],\n    columns: ['id', 'time', 'content', 'views'],\n    func: async (args) => {\n        const limit = Math.max(1, Math.min(Number(args.limit), 50));\n        const apiTag = TYPE_MAP[args.type] ?? 0;\n        const params = new URLSearchParams({\n            page: '1',\n            size: String(limit),\n            tag: String(apiTag),\n        });\n        const res = await fetch(`https://app.cj.sina.com.cn/api/news/pc?${params}`);\n        if (!res.ok) {\n            throw new CliError('FETCH_ERROR', `Sina Finance API HTTP ${res.status}`, 'Check your network connection');\n        }\n        const json = await res.json();\n        const list = json?.result?.data?.feed?.list ?? [];\n        if (!list.length) {\n            throw new CliError('NOT_FOUND', 'No news found', 'Try a different type or increase limit');\n        }\n        return list.map((item) => ({\n            id: item.id ?? '',\n            time: item.create_time ?? '',\n            content: stripHtml(item.rich_text ?? ''),\n            views: item.view_num ?? 0,\n        }));\n    },\n});\n","sourceCodeStart":30,"sourceCodeEnd":63,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/sinafinance/news.js#L30-L63","documentation":"The sinafinance news command fetches Sina Finance's 7x24 live-news API (app.cj.sina.com.cn/api/news/pc) with no auth. On any non-ok response it throws a CliError with code FETCH_ERROR, the HTTP status in the message, and a hint to check the network. It distinguishes transport-level rejection from the separate NOT_FOUND case (empty feed).","triggerScenarios":"fetch to https://app.cj.sina.com.cn/api/news/pc?page=1&size=N&tag=T returns 403 (WAF/blocked IP), 429 (throttled), or 5xx (Sina backend outage); also DNS/proxy failures would surface differently (network throw, not this error).","commonSituations":"Accessing from non-CN IPs that Sina's CDN throttles; bursts of polling requests hitting rate limits; Sina API maintenance windows; oversized `size` params triggering rejection.","solutions":["Retry with exponential backoff, especially for 429/5xx.","Check network connectivity and any proxy/VPN that may be blocked by Sina.","Keep `size` within 1–50 as the command already caps it; avoid excessive polling frequency.","Try a different `type`/tag in case a specific category endpoint is failing.","Wait out Sina-side maintenance and verify the endpoint is still live."],"exampleFix":"// before\nconst res = await fetch(`https://app.cj.sina.com.cn/api/news/pc?${params}`);\nif (!res.ok) throw new CliError('FETCH_ERROR', `Sina Finance API HTTP ${res.status}`, 'Check your network connection');\n// after: single retry with backoff before failing\nlet res = await fetch(`https://app.cj.sina.com.cn/api/news/pc?${params}`);\nif (!res.ok && (res.status === 429 || res.status >= 500)) {\n  await new Promise(r => setTimeout(r, 1500));\n  res = await fetch(`https://app.cj.sina.com.cn/api/news/pc?${params}`);\n}\nif (!res.ok) throw new CliError('FETCH_ERROR', `Sina Finance API HTTP ${res.status}`, 'Check your network connection');","handlingStrategy":"retry","validationCode":"const limit = Math.max(1, Math.min(Number(args.limit) || 20, 50));\nconst type = Number.isInteger(args.type) && args.type >= 0 && args.type <= 9 ? args.type : 0;\nif (!Number.isFinite(limit) || limit < 1) throw new Error('limit must be 1-50');","typeGuard":null,"tryCatchPattern":"try {\n  const feed = await fetchNews(limit, type);\n} catch (err) {\n  if (err.code === 'FETCH_ERROR') {\n    const m = /HTTP (\\d{3})/.exec(err.message);\n    if (m && (+m[1] === 429 || +m[1] >= 500)) return retryWithBackoff(() => fetchNews(limit, type), 3);\n  }\n  throw err;\n}","preventionTips":["Poll at modest intervals; this public feed throttles aggressive clients.","Keep `size` within 1–50 as the command enforces.","Use retries with backoff for 429/5xx responses.","Check network/proxy reachability to app.cj.sina.com.cn.","Distinguish FETCH_ERROR (transport) from NOT_FOUND (empty feed) when handling."],"tags":["http","network","api","sina","fetch-error"],"backgroundTag":"http-non-ok-status","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}