{"record":{"id":"5e0c90b053559e0e","repo":"jackwener/OpenCLI","slug":"label-returned-http-429-rate-limited-5e0c90","errorCode":null,"errorMessage":"${label} returned HTTP 429 (rate limited)","messagePattern":"(.+?) returned HTTP 429 \\(rate limited\\)","errorType":"http","errorClass":"CommandExecutionError","httpStatus":429,"severity":"warning","filePath":"clis/dblp/utils.js","lineNumber":45,"sourceCode":" * Wraps `fetch` with typed errors. We always set a UA per dblp's\n * polite-fetch guidance (https://dblp.org/faq/How+to+use+the+dblp+search+API.html).\n */\nasync function dblpFetch(url, label, accept) {\n    let res;\n    try {\n        res = await fetch(url, {\n            headers: {\n                accept,\n                'user-agent': 'opencli-dblp/1.0 (+https://github.com/jackwener/opencli)',\n            },\n        });\n    }\n    catch (err) {\n        throw new CommandExecutionError(`${label} request failed: ${err?.message ?? err}`, 'Check that dblp.org is reachable from this network.');\n    }\n    if (!res.ok) {\n        if (res.status === 429) {\n            throw new CommandExecutionError(`${label} returned HTTP 429 (rate limited)`, 'dblp throttles clients that fetch too aggressively. Wait a few seconds and retry, or lower --limit.');\n        }\n        if (res.status === 404) {\n            throw new EmptyResultError(label, 'dblp returned 404 — the requested record may not exist.');\n        }\n        throw new CommandExecutionError(`${label} returned HTTP ${res.status}`, 'Inspect the response in a browser at the same URL for more context.');\n    }\n    return res;\n}\n\nexport async function dblpFetchJson(path, label) {\n    const res = await dblpFetch(`${DBLP_ORIGIN}${path}`, label, 'application/json');\n    let body;\n    try {\n        body = await res.json();\n    }\n    catch (err) {\n        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);\n    }","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/dblp/utils.js#L27-L63","documentation":"CommandExecutionError thrown by dblpFetch when dblp.org responds with HTTP 429 Too Many Requests. dblp rate-limits clients that issue requests too quickly; the library surfaces this explicitly with guidance because 429 is common and recoverable.","triggerScenarios":"Any dblp subcommand executed repeatedly in quick succession — e.g. scripting many `dblp author`/`dblp paper` calls in a tight loop without delay, possibly shared across users behind one IP (CI runners, NAT).","commonSituations":"Batch scripts iterating over many authors/papers; parallel CI jobs hammering dblp from one IP; shared office/campus IP already throttled; aggressive retry loops.","solutions":["Wait a few seconds and retry — 429 is temporary","Lower --limit or reduce request frequency; add a sleep between calls","Serialize requests instead of running them in parallel","Cache results locally to avoid refetching the same records","If in CI, add backoff/jitter or route through a less-throttled network"],"exampleFix":"// before\nfor (const name of names) { await run(`dblp author --name ${name}`); }\n// Error: dblp author search returned HTTP 429 (rate limited)\n// after\nfor (const name of names) {\n  await run(`dblp author --name ${name}`);\n  await new Promise(r => setTimeout(r, 2000));\n}","handlingStrategy":"retry","validationCode":"// Throttle proactively so 429 never happens.\nconst limiter = pLimit(1); // serialize dblp calls\nconst throttled = (fn) => (...a) => limiter(async () => {\n  await sleep(1500);\n  return fn(...a);\n});","typeGuard":null,"tryCatchPattern":"async function withBackoff(fn, tries = 4) {\n  for (let i = 0; i < tries; i++) {\n    try { return await fn(); }\n    catch (err) {\n      if (/HTTP 429/.test(err.message) && i < tries - 1) {\n        await sleep(2 ** i * 1000);\n        continue;\n      }\n      throw err;\n    }\n  }\n}","preventionTips":["Space out dblp requests (≥1–2s) in scripts and CI","Never fire parallel requests at dblp from one IP","Cache fetched records to avoid repeat lookups","Honor a global rate limiter shared across all dblp calls"],"tags":["dblp","rate-limit","http-429"],"backgroundTag":"http-429-rate-limited","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}