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

  1. Log resp.status to identify whether it's 429/403 (rate/bot limit) vs 5xx (Google-side) and act accordingly
  2. If 429/403, back off, slow the polling interval, or switch IP / use a proxy
  3. If 5xx, retry after a delay — it's usually transient on Google's side
  4. Verify the constructed URL: valid lang, gl region, and ceid parameters (bad params can yield 4xx)
  5. 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

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/e70016b3b5920c59. Report an issue: GitHub.