{"record":{"id":"0548b383e5daaf60","repo":"jackwener/OpenCLI","slug":"label-http-response-status","errorCode":null,"errorMessage":"${label} HTTP ${response.status}","messagePattern":"\\$\\{label\\} HTTP \\$\\{response\\.status\\}","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/pubmed/utils.js","lineNumber":106,"sourceCode":"    if (waitMs > 0) {\n        await new Promise(resolve => setTimeout(resolve, waitMs));\n    }\n    lastRequestAt = Date.now();\n}\n\nexport async function eutilsFetch(tool, params = {}, { retmode = 'json', label = 'PubMed E-utilities' } = {}) {\n    const url = buildEutilsUrl(tool, { ...params, retmode });\n    await waitForRateLimit();\n    let response;\n    try {\n        response = await fetch(url);\n    }\n    catch (error) {\n        const detail = error instanceof Error ? error.message : String(error);\n        throw new CommandExecutionError(`${label} request failed`, detail);\n    }\n    if (!response.ok) {\n        throw new CommandExecutionError(`${label} HTTP ${response.status}`, 'Check NCBI availability, request parameters, and optional NCBI_API_KEY.');\n    }\n    if (retmode === 'xml') {\n        return response.text();\n    }\n    try {\n        const json = await response.json();\n        assertNoEutilsError(json, label);\n        return json;\n    }\n    catch (error) {\n        if (error instanceof CommandExecutionError) {\n            throw error;\n        }\n        const detail = error instanceof Error ? error.message : String(error);\n        throw new CommandExecutionError(`${label} returned invalid JSON`, detail);\n    }\n}\n","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/pubmed/utils.js#L88-L124","documentation":"After a successful fetch, eutilsFetch checks response.ok; a non-2xx status (429 rate limit, 5xx NCBI errors, 403) throws CommandExecutionError '<label> HTTP <status>' with a hint about NCBI availability, parameters, and the optional NCBI_API_KEY. Called by the xml, esearch, and result paths.","triggerScenarios":"Any pubmed command when NCBI returns 429 from exceeding 3 requests/second without an API key, 5xx during NCBI incidents, or 4xx from malformed eutils parameters.","commonSituations":"Burst-running many searches in a loop without an NCBI_API_KEY, NCBI maintenance windows, invalid tool/email parameters, blocked user agents.","solutions":["Request a free NCBI_API_KEY and export it (raises the limit from 3 to 10 req/s)","Add delays between sequential calls (e.g. sleep 400ms) to respect rate limits","Retry with backoff on 429/5xx; check NCBI status on persistent 5xx","Review request parameters if the status is 4xx"],"exampleFix":"// before\nfor (const q of queries) await pubmedSearch(q); // bursts >3 req/s\n// after\nexport NCBI_API_KEY=your_key_here\nfor (const q of queries) {\n  await pubmedSearch(q);\n  await new Promise(r => setTimeout(r, 150));\n}","handlingStrategy":"retry","validationCode":"// space requests to stay under 3 req/s without an API key\nawait new Promise(r => setTimeout(r, 400)); // between calls\n// and export NCBI_API_KEY=... to allow 10 req/s","typeGuard":null,"tryCatchPattern":"async function withStatusRetry(fn, attempts = 3) {\n  for (let i = 1; i <= attempts; i++) {\n    try { return await fn(); }\n    catch (err) {\n      const m = /HTTP (\\d+)/.exec(err.message ?? '');\n      const status = m && Number(m[1]);\n      if (status && (status === 429 || status >= 500) && i < attempts) {\n        await new Promise(r => setTimeout(r, 2 ** i * 1000));\n        continue;\n      }\n      throw err;\n    }\n  }\n}","preventionTips":["Register for an NCBI_API_KEY and export it in your environment","Throttle sequential calls to stay under the per-second rate limit","Back off exponentially on 429/5xx responses","Keep tool/email parameters valid and check NCBI status on persistent 5xx"],"tags":["http-status","rate-limit","ncbi","eutils"],"backgroundTag":"http-429-rate-limit","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}