{"record":{"id":"d92240c7fefc82d4","repo":"jackwener/OpenCLI","slug":"label-request-failed-err-message-err-d92240","errorCode":null,"errorMessage":"${label} request failed: ${err?.message ?? err}","messagePattern":"(.+?) request failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"critical","filePath":"clis/openalex/utils.js","lineNumber":87,"sourceCode":"        return `doi:${doiUrl[1]}`;\n    }\n    // 5) bare 10.xxxx/yyy DOI\n    if (DOI_BARE.test(raw)) {\n        return `doi:${raw}`;\n    }\n    throw new ArgumentError(\n        `openalex work id \"${value}\" is not recognised`,\n        'Use a Work id (\"W2741809807\"), a DOI (\"10.7717/peerj.4375\"), or a full openalex.org / doi.org URL.',\n    );\n}\n\nexport async function openalexFetch(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 api.openalex.org is reachable from this network.',\n        );\n    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, `OpenAlex returned 404 for ${url}.`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'OpenAlex throttles unauthenticated traffic; wait a few seconds and retry, or set OPENALEX_MAILTO.',\n        );\n    }\n    if (!resp.ok) {\n        let detail = '';\n        try {\n            const text = await resp.text();\n            const match = text.match(/\"message\"\\s*:\\s*\"([^\"]+)\"/);","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/openalex/utils.js#L69-L105","documentation":"openalexFetch wraps all HTTP access to api.openalex.org and throws CommandExecutionError when the fetch itself rejects (network-level failure, before any HTTP status is seen). The message includes the labeled operation and the underlying error, with a hint to check reachability of api.openalex.org. This distinguishes transport failure from HTTP error statuses like 404 or 429.","triggerScenarios":"fetch() rejecting due to DNS failure, no route to host, TLS errors, connection refused/timeout, or an offline network while calling any openalex command (search, ref, etc.).","commonSituations":"Corporate proxy or firewall blocking api.openalex.org; VPN required but not connected; DNS misconfiguration; IPv6 issues; ephemeral network drops in CI.","solutions":["Check network connectivity and that https://api.openalex.org resolves/reachable (curl it)","Configure proxy env vars (HTTPS_PROXY) if behind a corporate proxy","Retry after a short wait if it was a transient drop; consider adding a timeout/retry wrapper","Inspect the inner err.message in the error text for the specific socket/DNS cause"],"exampleFix":"// before\nconst works = await openalexSearch(q); // throws on any network blip\n// after\nconst works = await withRetry(3, () => openalexSearch(q));\nfunction withRetry(n, fn) {\n  return fn().catch(e => n > 1 ? withRetry(n - 1, fn) : Promise.reject(e));\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const data = await openalexFetch(url, 'openalex work');\n} catch (e) {\n  if (e.name === 'CommandExecutionError' && e.message.includes('request failed')) {\n    // network-level failure: check connectivity/proxy, retry with backoff\n    await sleep(1000);\n    return openalexFetch(url, 'openalex work');\n  }\n  throw e;\n}","preventionTips":["Set HTTPS_PROXY/HTTP_PROXY in restricted networks","Wrap API calls in a retry helper with exponential backoff","Verify api.openalex.org reachability (curl) as an environment smoke test","Surface the inner err.message when logging for faster diagnosis"],"tags":["openalex","network","fetch-error"],"backgroundTag":"connection-refused","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}