{"record":{"id":"98702bcc6ac95b40","repo":"jackwener/OpenCLI","slug":"label-returned-http-429-rate-limited-98702b","errorCode":null,"errorMessage":"${label} returned HTTP 429 (rate limited)","messagePattern":"(.+?) returned HTTP 429 \\(rate limited\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":429,"severity":"warning","filePath":"clis/npm/utils.js","lineNumber":60,"sourceCode":"    return n;\n}\n\nexport async function npmFetch(url, label) {\n    let resp;\n    try {\n        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });\n    }\n    catch (err) {\n        throw new CommandExecutionError(\n            `${label} request failed: ${err?.message ?? err}`,\n            'Check that registry.npmjs.org / api.npmjs.org are reachable from this network.',\n        );\n    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `npm registry returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'npm throttles unauthenticated bursts; wait a few seconds and retry.',\n        );\n    }\n    if (!resp.ok) {\n        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);\n    }\n    let body;\n    try {\n        body = await resp.json();\n    }\n    catch (err) {\n        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);\n    }\n    return body;\n}\n","sourceCodeStart":42,"sourceCodeEnd":77,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/npm/utils.js#L42-L77","documentation":"npmFetch treats HTTP 429 as a distinct rate-limiting condition. npm's public registry throttles unauthenticated clients that make too many requests in a short burst, so when a 429 status is returned the helper throws CommandExecutionError with remediation guidance. This lets callers know the failure is transient and retrying after a delay should succeed.","triggerScenarios":"A burst of npmFetch calls to registry.npmjs.org or api.npmjs.org without authentication exceeding npm's per-IP/per-client rate budget — e.g. looping over dozens of package names, running the CLI in CI across many parallel jobs, or shared CI runner IPs already exhausted by other tenants.","commonSituations":"CI pipelines (GitHub Actions, shared runners) hitting npm from well-known throttled IP ranges; scripts iterating over a large package list without delay or backoff; multiple developer tools on the same network all polling the registry simultaneously.","solutions":["Wait a few seconds and retry — npm throttles unauthenticated bursts, so a short backoff usually clears it","Add exponential backoff with jitter around npmFetch calls (e.g. retry after 1s, 2s, 4s)","Reduce request volume: batch/limit the number of packages queried per run, or add a delay between requests","Use an authenticated npm token where the API supports it, to raise the rate ceiling"],"exampleFix":"// before\nconst data = await npmFetch(url, 'npm package');\n// after\nasync function fetchWithBackoff(url, label, retries = 3) {\n  for (let i = 0; i < retries; i++) {\n    try {\n      return await npmFetch(url, label);\n    } catch (err) {\n      if (!String(err.message).includes('429') || i === retries - 1) throw err;\n      await new Promise((r) => setTimeout(r, 1000 * 2 ** i));\n    }\n  }\n}","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  return await npmFetch(url, label);\n} catch (err) {\n  if (/HTTP 429/.test(String(err.message))) {\n    await sleep(2000); // or retry with exponential backoff\n    return await npmFetch(url, label);\n  }\n  throw err;\n}","preventionTips":["Add exponential backoff with jitter to all registry polling loops","Throttle concurrency: cap parallel npmFetch calls to a small number","Avoid large batch queries from shared CI runner IPs; space out scheduled jobs","Use authenticated requests where the endpoint supports tokens to raise rate limits"],"tags":["npm","rate-limit","http-429","retry"],"backgroundTag":"rate-limited-429","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}