{"record":{"id":"dd0bc036d53cfc9e","repo":"jackwener/OpenCLI","slug":"label-request-failed-err-message-err-dd0bc0","errorCode":null,"errorMessage":"${label} request failed: ${err?.message ?? err}","messagePattern":"(.+?) request failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/npm/utils.js","lineNumber":51,"sourceCode":"export function requireBoundedInt(value, defaultValue, maxValue, label = 'limit') {\n    const raw = value ?? defaultValue;\n    const n = typeof raw === 'number' ? raw : Number(raw);\n    if (!Number.isInteger(n) || n <= 0) {\n        throw new ArgumentError(`npm ${label} must be a positive integer`);\n    }\n    if (n > maxValue) {\n        throw new ArgumentError(`npm ${label} must be <= ${maxValue}`);\n    }\n    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 {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/npm/utils.js#L33-L69","documentation":"npmFetch wraps the underlying fetch call; when fetch itself rejects (DNS failure, connection refused/reset, TLS error, offline) it throws CommandExecutionError `${label} request failed: ${...}` with a hint to check registry.npmjs.org / api.npmjs.org reachability. This is only for network-level failures — HTTP error statuses are handled separately (e.g. 404 becomes EmptyResultError).","triggerScenarios":"Any registry request while offline, behind a blocking firewall/proxy, with broken DNS, a mistyped NPM_REGISTRY/NPM_API base URL, corporate TLS interception with an untrusted CA, or IPv6 issues — i.e. whenever `fetch()` throws instead of returning a response.","commonSituations":"CI with no network egress; corporate proxy requiring configuration (undici fetch ignores HTTP_PROXY env vars by default); DNS blocked for npmjs.org; VPN down; self-signed MITM proxy without NODE_EXTRA_CA_CERTS.","solutions":["Verify network access: `curl -sI https://registry.npmjs.org` from the same host.","Fix proxy setup — configure an undici ProxyAgent/dispatcher or run outside the proxy.","Trust the corporate CA via NODE_EXTRA_CA_CERTS if TLS interception causes failures.","Check NPM_REGISTRY / NPM_API environment overrides for typos (protocol, spelling, trailing slashes).","Catch CommandExecutionError and retry with backoff for transient failures; surface the included hint to the user."],"exampleFix":"// before\nawait npmDownloads({ name: 'react', period: 'last-week' }); // offline -> CommandExecutionError\n// after\ntry {\n  return await npmDownloads({ name: 'react', period: 'last-week' });\n} catch (e) {\n  if (e.name === 'CommandExecutionError' && /request failed/.test(e.message)) {\n    await new Promise((r) => setTimeout(r, 1000)); // retry transient network failure\n    return await npmDownloads({ name: 'react', period: 'last-week' });\n  }\n  throw e;\n}","handlingStrategy":"retry","validationCode":"// cheap pre-flight reachability check\nconst online = await fetch('https://registry.npmjs.org/-/ping', { method: 'HEAD' })\n  .then(() => true)\n  .catch(() => false);\nif (!online) throw new Error('npm registry unreachable from this network');","typeGuard":"function isNetworkFailure(err) {\n  return err instanceof Error &&\n    (err.name === 'CommandExecutionError' || /request failed|ENOTFOUND|ECONNREFUSED|ETIMEDOUT|fetch failed/.test(String(err.message)));\n}","tryCatchPattern":"async function withRetry(fn, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await fn();\n    } catch (e) {\n      if (isNetworkFailure(e) && i < attempts - 1) {\n        await new Promise((r) => setTimeout(r, 2 ** i * 500));\n        continue;\n      }\n      throw e;\n    }\n  }\n}\nconst pkg = await withRetry(() => npmPackage({ name: 'react' }));","preventionTips":["Check network/VPN/proxy reachability to registry.npmjs.org and api.npmjs.org before batch runs.","Configure an undici ProxyAgent/dispatcher behind corporate proxies (fetch ignores HTTP_PROXY by default).","Set NODE_EXTRA_CA_CERTS for TLS-intercepting proxies.","Validate any NPM_REGISTRY/NPM_API base-URL overrides for typos.","Use exponential backoff for transient failures instead of failing the whole batch."],"tags":["network","fetch","npm","registry-unreachable","command-execution-error"],"backgroundTag":"network-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}