{"record":{"id":"e70016b3b5920c59","repo":"jackwener/OpenCLI","slug":"fetch-error-e70016","errorCode":"FETCH_ERROR","errorMessage":"HTTP ${resp.status}","messagePattern":"HTTP \\$\\{resp\\.status\\}","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/google/news.js","lineNumber":33,"sourceCode":"    args: [\n        { name: 'keyword', positional: true, help: 'Search query (omit for top stories)' },\n        { name: 'limit', type: 'int', default: 10, help: 'Number of results' },\n        { name: 'lang', default: 'en', help: 'Language short code (e.g. en, zh)' },\n        { name: 'region', default: 'US', help: 'Region code (e.g. US, CN)' },\n    ],\n    columns: ['title', 'source', 'date', 'url'],\n    func: async (args) => {\n        const limit = Math.max(1, Math.min(Number(args.limit), 100));\n        const lang = encodeURIComponent(args.lang);\n        const region = encodeURIComponent(args.region);\n        const ceid = `${args.region}:${args.lang}`;\n        // Top stories or search\n        const base = args.keyword\n            ? `https://news.google.com/rss/search?q=${encodeURIComponent(args.keyword)}&hl=${lang}&gl=${region}&ceid=${ceid}`\n            : `https://news.google.com/rss?hl=${lang}&gl=${region}&ceid=${ceid}`;\n        const resp = await fetch(base);\n        if (!resp.ok) {\n            throw new CliError('FETCH_ERROR', `HTTP ${resp.status}`, 'Check your network connection');\n        }\n        const xml = await resp.text();\n        const items = parseRssItems(xml, ['title', 'link', 'pubDate', 'source']);\n        if (!items.length) {\n            throw new CliError('NOT_FOUND', 'No news articles found', 'Try a different keyword or region');\n        }\n        return items.slice(0, limit).map(item => {\n            // Extract source: prefer <source> element, fallback to parsing title\n            let title = item['title'] || '';\n            let source = item['source'] || '';\n            if (!source) {\n                const idx = title.lastIndexOf(' - ');\n                if (idx !== -1) {\n                    source = title.slice(idx + 3);\n                    title = title.slice(0, idx);\n                }\n            }\n            return {","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/google/news.js#L15-L51","documentation":"The google news command fetches the Google News RSS feed (top stories or keyword search) and throws this CliError with code FETCH_ERROR when the HTTP response is not ok. It wraps any non-2xx status from news.google.com into a single user-facing error with a hint to check the network connection.","triggerScenarios":"fetch() to https://news.google.com/rss (or /rss/search) completes but resp.ok is false — e.g. HTTP 429 rate-limit, 403 bot rejection, 5xx from Google, or a captive proxy returning an error page.","commonSituations":"Polling the RSS endpoint too frequently from one IP and getting rate-limited; corporate proxy or firewall returning 403/502; temporary Google News outage (503); malformed region/ceid parameters causing 400.","solutions":["Log resp.status to identify whether it's 429/403 (rate/bot limit) vs 5xx (Google-side) and act accordingly","If 429/403, back off, slow the polling interval, or switch IP / use a proxy","If 5xx, retry after a delay — it's usually transient on Google's side","Verify the constructed URL: valid lang, gl region, and ceid parameters (bad params can yield 4xx)","Check local network/proxy connectivity if every request fails"],"exampleFix":"// before\nif (!resp.ok) throw new CliError('FETCH_ERROR', `HTTP ${resp.status}`, 'Check your network connection');\n// after\nif (!resp.ok) {\n  if (resp.status === 429) await new Promise(r => setTimeout(r, 30000));\n  throw new CliError('FETCH_ERROR', `HTTP ${resp.status}`, 'Check your network connection');\n}","handlingStrategy":"retry","validationCode":"// check reachability before calling the command\nconst head = await fetch('https://news.google.com/rss', {method: 'HEAD'});\nif (!head.ok) throw new Error('Google News unreachable: ' + head.status);","typeGuard":"function isHttpError(resp) {\n  return typeof Response !== 'undefined' ? resp instanceof Response && !resp.ok : false;\n}","tryCatchPattern":"try {\n  const news = await newsCommand.func(args);\n} catch (e) {\n  if (e.code === 'FETCH_ERROR' && e.message.includes('429')) {\n    await sleep(30000);\n    return newsCommand.func(args);\n  }\n  throw e;\n}","preventionTips":["Poll RSS feeds at respectful intervals to avoid 429","Verify lang/gl/ceid URL parameters are valid","Handle 5xx with delayed retries","Log resp.status to distinguish rate limits from outages"],"tags":["network","http","rss","google"],"backgroundTag":"http-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}