{"record":{"id":"83e2854d52777ee1","repo":"jackwener/OpenCLI","slug":"openalex-returned-404-for-url","errorCode":null,"errorMessage":"OpenAlex returned 404 for ${url}.","messagePattern":"OpenAlex returned 404 for (.+?)\\.","errorType":"exception","errorClass":"EmptyResultError","httpStatus":404,"severity":"error","filePath":"clis/openalex/utils.js","lineNumber":93,"sourceCode":"    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*\"([^\"]+)\"/);\n            if (match) detail = ` (${match[1]})`;\n        }\n        catch { /* ignore */ }\n        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}${detail}`);\n    }\n    let body;","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/openalex/utils.js#L75-L111","documentation":"openalexFetch maps an HTTP 404 from api.openalex.org to an EmptyResultError with the full request URL in the message. For the OpenAlex API, 404 means the requested entity path does not exist — typically an unrecognized or non-existent work ID/DOI. It is typed as EmptyResultError (not a transport failure) so callers can treat it like 'nothing found'.","triggerScenarios":"Calling a work lookup with a ref that resolves to a canonical path OpenAlex has no record of — e.g. a well-formed but wrong/nonexistent W-id or DOI — producing a 404 from /works/<id> or /works/doi:….","commonSituations":"Hallucinated or fabricated W-ids; typos in a DOI; a DOI that was never registered with Crossref/OpenAlex; using an ID from a different provider (Semantic Scholar, MAG) that doesn't exist in OpenAlex.","solutions":["Verify the W-id/DOI exists by opening https://openalex.org/<id> or https://doi.org/<doi> in a browser","Re-lookup the identifier via the openalex search command and use the returned id","Check the ref wasn't mangled (truncated digits, wrong DOI suffix)","If the DOI is correct but unindexed, the work may not be in OpenAlex — use an alternate source"],"exampleFix":"// before\nawait work('W9999999999'); // nonexistent\n// after\nconst [hit] = await search('attention is all you need');\nif (hit) await work(hit.id);","handlingStrategy":"try-catch","validationCode":"function isPlausibleWorkId(v) {\n  return /^W\\d{4,}$/.test(String(v).trim().toUpperCase());\n}\nfunction isPlausibleDoi(v) {\n  return /^10\\.\\S+$/.test(String(v).trim());\n}\nif (!isPlausibleWorkId(ref) && !isPlausibleDoi(ref)) {\n  throw new Error('ref must be a W-id or DOI before lookup');\n}","typeGuard":null,"tryCatchPattern":"try {\n  const w = await work(ref);\n} catch (e) {\n  if (e.name === 'EmptyResultError' && e.message.includes('404')) {\n    console.warn(`Work not found for ${ref}; skipping`);\n    return null;\n  }\n  throw e;\n}","preventionTips":["Verify IDs on openalex.org before bulk processing","Re-resolve uncertain identifiers via the search endpoint","Handle EmptyResultError distinctly so one bad ID doesn't abort a batch"],"tags":["openalex","http-404","not-found"],"backgroundTag":"http-404-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}