jackwener/OpenCLI · warning · CommandExecutionError

${label} returned HTTP 429 (rate limited)

Error message

${label} returned HTTP 429 (rate limited)

What it means

restCountriesFetch throws CommandExecutionError when the REST Countries API responds with HTTP 429, meaning the client exceeded the service's rate limit. The library surfaces the 429 as a distinct, explicitly-named 'rate limited' error so callers know the failure is temporary and quota-related, not a bad query or outage.

Source

Thrown at clis/rest-countries/utils.js:67

    return raw;
}

export async function restCountriesFetch(url, label) {
    let resp;
    try {
        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });
    }
    catch (err) {
        throw new CommandExecutionError(
            `${label} request failed: ${err?.message ?? err}`,
            'Check that restcountries.com is reachable from this network.',
        );
    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `REST Countries returned 404 for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(`${label} returned HTTP 429 (rate limited)`);
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
    }
    let body;
    try {
        body = await resp.json();
    }
    catch (err) {
        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
    }
    return body;
}

/** Convert REST Countries' `{cur: {name, symbol}}` map to a comma-joined list. */
export function joinCurrencies(currencies) {
    if (!currencies || typeof currencies !== 'object') return '';
    return Object.entries(currencies)

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Wait (e.g. 30-60 seconds) and retry the same command, ideally with backoff
  2. Add a delay between consecutive rest-countries calls in batch scripts
  3. Cache previously fetched country data locally to reduce repeat API calls
  4. Check whether a shared proxy/VPN IP is causing the throttling and use a different egress

Example fix

// before
for (const c of ['france','germany','spain']) await getCountry(c);
// after
for (const c of ['france','germany','spain']) {
  await getCountry(c);
  await new Promise(r => setTimeout(r, 1500)); // stay under rate limit
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try {
  await restCountriesCommand(args);
} catch (e) {
  if (String(e.message).includes('429')) {
    await sleep(60000); // backoff, then retry once
    return await restCountriesCommand(args);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling any rest-countries command (which internally calls restCountriesFetch) repeatedly in a short window, or sharing an IP/proxy with other heavy users of restcountries.com, until the API throttles the request with a 429 response.

Common situations: Batch scripts iterating over many country lookups without delay; CI jobs making parallel requests; shared corporate/VPN egress IP already rate-limited by REST Countries' free-tier quota.

Understand the failure class

Related errors


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