jackwener/OpenCLI · error · EmptyResultError

OpenAlex returned 404 for ${url}.

Error message

OpenAlex returned 404 for ${url}.

What it means

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'.

Source

Thrown at clis/openalex/utils.js:93

    throw new ArgumentError(
        `openalex work id "${value}" is not recognised`,
        'Use a Work id ("W2741809807"), a DOI ("10.7717/peerj.4375"), or a full openalex.org / doi.org URL.',
    );
}

export async function openalexFetch(url, label) {
    let resp;
    try {
        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });
    }
    catch (err) {
        throw new CommandExecutionError(
            `${label} request failed: ${err?.message ?? err}`,
            'Check that api.openalex.org is reachable from this network.',
        );
    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `OpenAlex returned 404 for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'OpenAlex throttles unauthenticated traffic; wait a few seconds and retry, or set OPENALEX_MAILTO.',
        );
    }
    if (!resp.ok) {
        let detail = '';
        try {
            const text = await resp.text();
            const match = text.match(/"message"\s*:\s*"([^"]+)"/);
            if (match) detail = ` (${match[1]})`;
        }
        catch { /* ignore */ }
        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}${detail}`);
    }
    let body;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the W-id/DOI exists by opening https://openalex.org/<id> or https://doi.org/<doi> in a browser
  2. Re-lookup the identifier via the openalex search command and use the returned id
  3. Check the ref wasn't mangled (truncated digits, wrong DOI suffix)
  4. If the DOI is correct but unindexed, the work may not be in OpenAlex — use an alternate source

Example fix

// before
await work('W9999999999'); // nonexistent
// after
const [hit] = await search('attention is all you need');
if (hit) await work(hit.id);
Defensive patterns

Strategy: try-catch

Validate before calling

function isPlausibleWorkId(v) {
  return /^W\d{4,}$/.test(String(v).trim().toUpperCase());
}
function isPlausibleDoi(v) {
  return /^10\.\S+$/.test(String(v).trim());
}
if (!isPlausibleWorkId(ref) && !isPlausibleDoi(ref)) {
  throw new Error('ref must be a W-id or DOI before lookup');
}

Try / catch

try {
  const w = await work(ref);
} catch (e) {
  if (e.name === 'EmptyResultError' && e.message.includes('404')) {
    console.warn(`Work not found for ${ref}; skipping`);
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: 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:….

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/83e2854d52777ee1. Report an issue: GitHub.