jackwener/OpenCLI · error · CliError
FETCH_ERROR
FETCH_ERROR
Error message
HTTP ${resp.status} What it means
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.
Source
Thrown at clis/google/news.js:33
args: [
{ name: 'keyword', positional: true, help: 'Search query (omit for top stories)' },
{ name: 'limit', type: 'int', default: 10, help: 'Number of results' },
{ name: 'lang', default: 'en', help: 'Language short code (e.g. en, zh)' },
{ name: 'region', default: 'US', help: 'Region code (e.g. US, CN)' },
],
columns: ['title', 'source', 'date', 'url'],
func: async (args) => {
const limit = Math.max(1, Math.min(Number(args.limit), 100));
const lang = encodeURIComponent(args.lang);
const region = encodeURIComponent(args.region);
const ceid = `${args.region}:${args.lang}`;
// Top stories or search
const base = args.keyword
? `https://news.google.com/rss/search?q=${encodeURIComponent(args.keyword)}&hl=${lang}&gl=${region}&ceid=${ceid}`
: `https://news.google.com/rss?hl=${lang}&gl=${region}&ceid=${ceid}`;
const resp = await fetch(base);
if (!resp.ok) {
throw new CliError('FETCH_ERROR', `HTTP ${resp.status}`, 'Check your network connection');
}
const xml = await resp.text();
const items = parseRssItems(xml, ['title', 'link', 'pubDate', 'source']);
if (!items.length) {
throw new CliError('NOT_FOUND', 'No news articles found', 'Try a different keyword or region');
}
return items.slice(0, limit).map(item => {
// Extract source: prefer <source> element, fallback to parsing title
let title = item['title'] || '';
let source = item['source'] || '';
if (!source) {
const idx = title.lastIndexOf(' - ');
if (idx !== -1) {
source = title.slice(idx + 3);
title = title.slice(0, idx);
}
}
return {View on GitHub (pinned to 49907e53dc)
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
Example fix
// before
if (!resp.ok) throw new CliError('FETCH_ERROR', `HTTP ${resp.status}`, 'Check your network connection');
// after
if (!resp.ok) {
if (resp.status === 429) await new Promise(r => setTimeout(r, 30000));
throw new CliError('FETCH_ERROR', `HTTP ${resp.status}`, 'Check your network connection');
} Defensive patterns
Strategy: retry
Validate before calling
// check reachability before calling the command
const head = await fetch('https://news.google.com/rss', {method: 'HEAD'});
if (!head.ok) throw new Error('Google News unreachable: ' + head.status); Type guard
function isHttpError(resp) {
return typeof Response !== 'undefined' ? resp instanceof Response && !resp.ok : false;
} Try / catch
try {
const news = await newsCommand.func(args);
} catch (e) {
if (e.code === 'FETCH_ERROR' && e.message.includes('429')) {
await sleep(30000);
return newsCommand.func(args);
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- ${label} returned HTTP ${resp.status} (${url})
- FETCH_ERROR
- FETCH_ERROR
- 12306 queryByTrainNo returned HTTP ${resp.status}
- 12306 queryByTrainNo returned non-JSON body
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e70016b3b5920c59.
Report an issue: GitHub.