jackwener/OpenCLI · warning · CommandExecutionError

${label} returned HTTP 429 (rate limited)

Error message

${label} returned HTTP 429 (rate limited)

What it means

flathubFetch throws CommandExecutionError '<label> returned HTTP 429 (rate limited)' when flathub.org responds with 429, meaning the client exceeded Flathub's request rate limits. The library raises it as a distinct error so callers can recognize throttling and back off rather than hammering the API.

Source

Thrown at clis/flathub/utils.js:66

    let resp;
    try {
        resp = await fetch(url, {
            method: init?.method ?? 'GET',
            headers: { 'user-agent': UA, accept: 'application/json', ...(init?.headers ?? {}) },
            body: init?.body,
        });
    }
    catch (err) {
        throw new CommandExecutionError(
            `${label} request failed: ${err?.message ?? err}`,
            'Check that flathub.org is reachable from this network.',
        );
    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `Flathub 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;
}

export function joinList(value, max = 10) {
    if (!Array.isArray(value)) return '';
    const items = value.filter((v) => typeof v === 'string' && v.trim());
    if (items.length === 0) return '';

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Add a delay between requests (e.g. 1-2s sleep) or implement exponential backoff on 429
  2. Batch or deduplicate requests — cache appstream results locally instead of refetching
  3. Reduce concurrency: run requests sequentially or with a small parallelism cap
  4. Wait and retry later if a shared IP is being throttled

Example fix

// before
for (const id of ids) await appInfo(id); // hammers the API
// after
for (const id of ids) {
  await appInfo(id);
  await new Promise((r) => setTimeout(r, 1000));
}
Defensive patterns

Strategy: retry

Validate before calling

// Throttle before calling: at most 1 request per second
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
await sleep(1000); // between flathub calls in a loop

Type guard

null

Try / catch

async function withBackoff(fn, attempts = 4) {
  for (let i = 0; i < attempts; i++) {
    try { return await fn(); }
    catch (err) {
      if (!/HTTP 429/.test(err.message) || i === attempts - 1) throw err;
      await new Promise((r) => setTimeout(r, 2 ** i * 1000));
    }
  }
}

Prevention

When it happens

Trigger: Looping over many appIds calling the appstream endpoint in rapid succession; concurrent parallel requests from scripts/CI; retry loops without backoff after earlier failures; shared IP (CI runner, corporate NAT) already rate-limited.

Common situations: Batch scripts fetching metadata for hundreds of apps; automated CI jobs polling Flathub; multiple teammates behind one NAT IP; aggressive retry logic after network errors.

Understand the failure class

Related errors


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