{"record":{"id":"950c236f79cc813a","repo":"jackwener/OpenCLI","slug":"label-request-failed-err-message-err-950c23","errorCode":null,"errorMessage":"${label} request failed: ${err?.message ?? err}","messagePattern":"(.+?) request failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"critical","filePath":"clis/crates/utils.js","lineNumber":47,"sourceCode":"    const raw = value ?? defaultValue;\n    const n = typeof raw === 'number' ? raw : Number(raw);\n    if (!Number.isInteger(n) || n <= 0) {\n        throw new ArgumentError(`crates ${label} must be a positive integer`);\n    }\n    if (n > maxValue) {\n        throw new ArgumentError(`crates ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}\n\nexport async function cratesFetch(url, label) {\n    let resp;\n    try {\n        // crates.io requires a descriptive User-Agent per https://crates.io/data-access\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 crates.io is reachable from this network.',\n        );\n    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `crates.io returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'crates.io rate-limits unauthenticated traffic; 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":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/crates/utils.js#L29-L65","documentation":"cratesFetch wraps the underlying fetch to crates.io; if the request throws at the network layer (DNS failure, connection refused/reset, TLS error, offline), it rethrows as a CommandExecutionError with '<label> request failed: <cause>' and a hint to check reachability. It deliberately does not retry — transport errors surface immediately to the caller.","triggerScenarios":"Any crates.io call (`crates search`, `crates crate`) executed while offline, behind a blocking corporate proxy/firewall, with broken DNS, when crates.io is down, or in Node environments where fetch/HTTPS to crates.io is blocked (ECONNREFUSED, ENOTFOUND, CERT_HAS_EXPIRED, etc.).","commonSituations":"Working on a plane/VPN without internet, corporate proxies that block api crates.io traffic, IPv6 misconfiguration, expired local CA bundles in CI containers, or transient crates.io outages.","solutions":["Verify network connectivity: curl -I https://crates.io/api/v1/crates/serde.","Check proxy settings (HTTPS_PROXY/HTTP_PROXY) and corporate firewall rules; ensure fetch honors your proxy agent.","Retry after a short backoff if the outage is transient; add your own retry wrapper around the call.","Inspect the wrapped cause message (err?.message) for the specific socket/TLS error to target the fix.","In CI, confirm the container has CA certificates and DNS resolution working."],"exampleFix":"// before\nconst body = await cratesFetch(url, 'crates search'); // throws on first network blip\n// after\nconst body = await withRetry(3, () => cratesFetch(url, 'crates search'));\n// withRetry: attempts n times with exponential backoff, rethrowing the CommandExecutionError on final failure","handlingStrategy":"retry","validationCode":"// Pre-flight reachability check before batch calls:\nconst ok = await fetch('https://crates.io/api/v1/summary', { method: 'HEAD' })\n  .then(() => true)\n  .catch(() => false);\nif (!ok) throw new Error('crates.io unreachable — check network/proxy before running');","typeGuard":"function isNetworkCause(err) {\n  const msg = String(err?.cause?.message ?? err?.message ?? '');\n  return /ECONNREFUSED|ENOTFOUND|ETIMEDOUT|ECONNRESET|EAI_AGAIN|CERT|fetch failed/i.test(msg);\n}","tryCatchPattern":"async function fetchWithRetry(url, label, attempts = 3) {\n  for (let i = 1; i <= attempts; i++) {\n    try {\n      return await cratesFetch(url, label);\n    } catch (e) {\n      const retriable = !(e instanceof EmptyResultError) && i < attempts;\n      if (!retriable) throw e;\n      await new Promise(r => setTimeout(r, 2 ** i * 500));\n    }\n  }\n}","preventionTips":["Configure HTTPS_PROXY and a proxy-aware fetch agent in corporate networks.","Add exponential-backoff retries for transient socket errors.","Monitor crates.io status; batch jobs should fail fast on the first unreachable error.","Ensure CI containers have current CA certificates and working DNS.","Distinguish CommandExecutionError (transport) from EmptyResultError (not found) in error handling."],"tags":["network","fetch-failed","crates-io","connectivity"],"backgroundTag":"network-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}