{"record":{"id":"5724a52b1759c0af","repo":"jackwener/OpenCLI","slug":"network-failure-fetching-label-detail","errorCode":null,"errorMessage":"Network failure fetching ${label}: ${detail}","messagePattern":"Network failure fetching (.+?): (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/stackoverflow/read.js","lineNumber":35,"sourceCode":" *   - first row is the question itself (`type=POST`)\n *   - one row per top-level question comment (`type=Q-COMMENT`)\n *   - per answer: an `ANSWER` row plus its `A-COMMENT` rows indented under it\n *   - the accepted answer (if any) is surfaced first and tagged `accepted=true`\n */\nimport { cli, Strategy } from '@jackwener/opencli/registry';\nimport { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';\n\nconst SE_API_BASE = 'https://api.stackexchange.com/2.3';\nconst SE_SITE = 'stackoverflow';\nconst SE_MAX_PAGE_SIZE = 100;\n\nasync function fetchJson(url, label) {\n    let res;\n    try {\n        res = await fetch(url);\n    } catch (e) {\n        const detail = e instanceof Error ? e.message : String(e);\n        throw new CommandExecutionError(\n            `Network failure fetching ${label}: ${detail}`,\n            'Check connectivity to api.stackexchange.com',\n        );\n    }\n    if (res.status === 404) {\n        throw new EmptyResultError(label, `${label} not found`);\n    }\n    if (!res.ok) {\n        throw new CommandExecutionError(\n            `Stack Exchange API HTTP ${res.status} for ${label}`,\n            'Check the question id and quota (300/day per IP)',\n        );\n    }\n    let json;\n    try {\n        json = await res.json();\n    } catch (e) {\n        const detail = e instanceof Error ? e.message : String(e);","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/stackoverflow/read.js#L17-L53","documentation":"fetchJson in clis/stackoverflow/read.js wraps the initial fetch() call in try/catch and rethrows a CommandExecutionError when the request itself fails (DNS, TLS, connection refused, timeout), labeling the failing resource and including the underlying error message. It is thrown before any HTTP status is inspected, so the Stack Exchange API was never reached successfully.","triggerScenarios":"Calling any of fetchJson's callers (qData, answersData, acceptedData, qCommentsData, ansCommentsData) against api.stackexchange.com while fetch() rejects: offline network, DNS failure for api.stackexchange.com, firewall/proxy blocking the request, TLS interception, or the process being killed mid-request.","commonSituations":"Working behind a corporate proxy that Node's fetch doesn't honor; VPN drop or captive portal; DNS resolver misconfiguration in containers; ISP or corporate firewall blocking api.stackexchange.com; transient internet outage during a script run.","solutions":["Verify general connectivity: curl -I https://api.stackexchange.com/2.2/site/stackoverflow","Check DNS resolution of api.stackexchange.com (nslookup/dig); fix resolver or /etc/hosts if it fails","If behind a proxy, set HTTP_PROXY/HTTPS_PROXY (and NODE_USE_ENV_PROXY or an agent) so Node's fetch routes through it","Retry after a short backoff for transient outages; the error suggests 'Check connectivity to api.stackexchange.com'","Disable TLS-intercepting VPN/firewall temporarily to confirm it is the cause"],"exampleFix":"async function fetchWithRetry(url, retries = 3) {\n  for (let i = 0; i < retries; i++) {\n    try {\n      return await fetch(url);\n    } catch (e) {\n      if (i === retries - 1) throw e;\n      await new Promise(r => setTimeout(r, 1000 * 2 ** i));\n    }\n  }\n}","handlingStrategy":"retry","validationCode":"async function canReachStackExchange() {\n  try {\n    const res = await fetch('https://api.stackexchange.com/2.2/info?site=stackoverflow');\n    return res.ok;\n  } catch {\n    return false;\n  }\n}\n// check before batch calls: if (!(await canReachStackExchange())) abort;","typeGuard":null,"tryCatchPattern":"async function withNetworkRetry(fn, retries = 3) {\n  for (let i = 0; ; i++) {\n    try {\n      return await fn();\n    } catch (e) {\n      const isNetwork = /Network failure fetching/.test(e?.message ?? '');\n      if (!isNetwork || i >= retries) throw e;\n      await new Promise(r => setTimeout(r, 1000 * 2 ** i));\n    }\n  }\n}\nconst data = await withNetworkRetry(() => qData(id));","preventionTips":["Set HTTPS_PROXY/HTTP_PROXY when behind a corporate proxy so Node's fetch routes correctly","Check DNS for api.stackexchange.com in containers/CI before running batch scripts","Use exponential backoff on fetch failures instead of failing the whole batch","Verify VPN/captive-portal connectivity before long-running API scripts"],"tags":["network","fetch","stackexchange-api","connectivity","dns"],"backgroundTag":"network-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}