jackwener/OpenCLI · warning · CommandExecutionError

${label} returned HTTP 429 (rate limited)

Error message

${label} returned HTTP 429 (rate limited)

What it means

steamFetch detects HTTP 429 from Steam and throws a CommandExecutionError indicating the operation was rate limited, advising a short wait. Steam throttles bursty traffic against its store endpoints.

Source

Thrown at clis/steam/utils.js:66

            'Copy the numeric id from `steam search` or the URL `store.steampowered.com/app/<id>/`.',
        );
    }
    return s;
}

export async function steamFetch(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 store.steampowered.com is reachable from this network.',
        );
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'Steam throttles bursty traffic; wait a few seconds and retry.',
        );
    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, 'Steam returned 404 — the resource does not exist.');
    }
    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;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Wait a few seconds and retry the same command
  2. Add a delay/backoff between successive Steam requests in batch scripts
  3. Reduce batch size or spread lookups over time
  4. Cache results locally to avoid repeat requests for the same id

Example fix

// before
for (const id of ids) await steamFetch(appUrl(id), 'app details'); // bursts trigger 429
// after
for (const id of ids) {
  await steamFetch(appUrl(id), 'app details');
  await new Promise(r => setTimeout(r, 1000));
}
Defensive patterns

Strategy: retry

Validate before calling

// no pre-check possible; mitigate with throttling
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
await sleep(1000); // between consecutive steamFetch calls

Type guard

null

Try / catch

try { return await steamFetch(url, label); } catch (e) { if (/429/.test(e.message)) { await sleep(5000); return steamFetch(url, label); } throw e; }

Prevention

When it happens

Trigger: Hitting store.steampowered.com endpoints repeatedly in a tight loop: batch-fetching many app ids, re-running search commands rapidly, or sharing an IP with other Steam API traffic.

Common situations: Batch scripts iterating hundreds of app ids without delay, CI jobs with frequent Steam lookups, office networks where many users share one egress IP, retry storms after earlier failures.

Understand the failure class

Related errors


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