jackwener/OpenCLI · error · CommandExecutionError

stack exchange returned HTTP 429 (rate limited)

Error message

stack exchange returned HTTP 429 (rate limited)

What it means

CommandExecutionError thrown by seFetch when the Stack Exchange API responds with HTTP 429, meaning the client has exceeded the anonymous quota (300 requests/day per IP for unauthenticated calls, per the file's header comment). The suggestion field advises waiting or lowering --limit.

Source

Thrown at clis/stackoverflow/utils.js:63

            url.searchParams.set(k, String(v));
        }
    }
    if (!url.searchParams.has('site')) url.searchParams.set('site', SE_SITE);

    let resp;
    try {
        resp = await fetch(url, {
            headers: {
                'Accept': 'application/json',
                'Accept-Encoding': 'gzip',
                'User-Agent': UA,
            },
        });
    } catch (error) {
        throw new CommandExecutionError(`stack exchange request failed: ${error?.message || error}`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError('stack exchange returned HTTP 429 (rate limited)', 'Wait a few seconds and retry, or lower --limit.');
    }
    if (!resp.ok) {
        let body = '';
        try { body = (await resp.json())?.error_message || ''; } catch { /* ignore */ }
        throw new CommandExecutionError(`stack exchange HTTP ${resp.status}: ${body || resp.statusText}`);
    }
    let data;
    try {
        data = await resp.json();
    } catch (error) {
        throw new CommandExecutionError(`stack exchange returned malformed JSON: ${error?.message || error}`);
    }
    if (data?.error_id) {
        throw new CommandExecutionError(
            `stack exchange API error: ${data.error_message || data.error_name}`,
            'Inspect the URL in a browser for the canonical error context.',
        );
    }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Wait until the daily quota resets, then retry.
  2. Reduce request volume: lower --limit, batch ids via /questions/{ids};-separated paths, cache responses.
  3. Register a Stack Apps app key to raise the quota to 10,000 requests/day and pass key as a search param.
  4. Authenticate with OAuth for a 30,000/day quota on behalf of a user.
  5. Cache results locally so repeat invocations don't re-hit the API.

Example fix

// before: immediate retry loop
for (const id of ids) await read(id);
// after: batch + throttle
const chunks = chunk(ids, 100);
for (const c of chunks) {
  await seFetch(`/questions/${c.join(';')}?site=stackoverflow&filter=withbody`);
  await sleep(500);
}
Defensive patterns

Strategy: retry

Validate before calling

function withKey(url) { return url + (process.env.SE_APP_KEY ? `${url.includes('?') ? '&' : '?'}key=${process.env.SE_APP_KEY}` : ''); } // raises quota from 300 to 10,000/day

Try / catch

async function fetchRespecting429(path) {
  try {
    return await seFetch(path);
  } catch (e) {
    if (String(e.message).includes('HTTP 429')) {
      const retryAfter = 10 + Math.random() * 5;
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      return seFetch(path);
    }
    throw e;
  }
}

Prevention

When it happens

Trigger: More than ~300 api.stackexchange.com calls per day from one IP; a burst of automated queries/agents sharing an IP (CI runners, office NAT); repeatedly re-running searches with large pagesizes.

Common situations: CI pipelines hammering the API on every build; multiple team members behind one corporate egress IP; scripts looping over many question ids in a short window; shared cloud/VPN egress IPs that are already exhausted.

Understand the failure class

Related errors


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