jackwener/OpenCLI · warning · CommandExecutionError
${label} returned HTTP 429 (rate limited)
Error message
${label} returned HTTP 429 (rate limited) What it means
rfcFetch detects HTTP 429 from the IETF datatracker and throws CommandExecutionError noting the client is rate limited. The datatracker throttles clients making too many requests in a short window.
Source
Thrown at clis/rfc/utils.js:49
return n;
}
export async function rfcFetch(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 datatracker.ietf.org is reachable from this network.',
);
}
if (resp.status === 404) {
throw new EmptyResultError(label, `IETF datatracker 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;
}
// IETF datatracker timestamps are like "2022-02-19 08:46:51" (no T, no Z)
// or ISO with offset like "2016-07-08T21:03:52+00:00". Normalise to YYYY-MM-DD.
export function trimDate(value) {
const s = String(value ?? '').trim();View on GitHub (pinned to 49907e53dc)
Solutions
- Add a delay/backoff between requests (e.g. sleep 1s per fetch).
- Respect Retry-After semantics; retry the failed request after waiting.
- Cache previously fetched RFC metadata to avoid repeat calls.
- Catch CommandExecutionError and retry with exponential backoff.
Example fix
// before
for (const n of numbers) await fetchRfc(n);
// after
for (const n of numbers) {
await fetchRfc(n);
await new Promise(r => setTimeout(r, 1000));
} Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
async function fetchWithBackoff(url, label, maxAttempts = 5) {
for (let i = 0; i < maxAttempts; i++) {
try {
return await rfcFetch(url, label);
} catch (err) {
const is429 = err instanceof CommandExecutionError && err.message.includes('429');
if (!is429 || i === maxAttempts - 1) throw err;
await new Promise(r => setTimeout(r, Math.min(2 ** i * 1000, 30000)));
}
}
} Prevention
- Throttle bulk fetches (e.g. 1 request/second).
- Honor Retry-After headers when present.
- Cache responses to avoid repeated identical requests.
- Run heavy batch jobs off shared egress IPs or during off-peak hours.
When it happens
Trigger: Issuing many rapid rfc lookups (bulk scripts iterating hundreds of RFC numbers) until datatracker returns 429.
Common situations: Batch-fetching hundreds of RFCs in a loop, CI jobs hammering the API without delay, or shared-IP rate limits (office/CI egress IP throttled).
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- coingecko returned HTTP 429 (rate limited)
- coingecko returned HTTP 429 (rate limited)
- ${label} returned HTTP 429 (rate limited)
- stack exchange returned HTTP 429 (rate limited)
- coingecko returned HTTP 429 (rate limited)
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/6846c18147cc1e49.
Report an issue: GitHub.