jackwener/OpenCLI · warning · EmptyResultError

Semantic Scholar returned 404 for ${url}.

Error message

Semantic Scholar returned 404 for ${url}.

What it means

s2Fetch maps an HTTP 404 from the Semantic Scholar API to EmptyResultError with the full request URL in the message. Semantically, 404 means the requested resource (paper id, citation set, recommendation set) does not exist, so the library treats it as an empty result rather than a hard failure — enabling uniform no-data handling in scripts.

Source

Thrown at clis/semanticscholar/utils.js:113

    while (true) {
        try {
            resp = await fetch(url, { headers });
        } catch (err) {
            throw new CommandExecutionError(
                `${label} request failed: ${err?.message ?? err}`,
                'Check that api.semanticscholar.org is reachable from this network.',
            );
        }
        if (resp.status === 429 && attempt === 0 && !apiKey) {
            attempt += 1;
            await new Promise(resolve => setTimeout(resolve, 1500));
            continue;
        }
        break;
    }

    if (resp.status === 404) {
        throw new EmptyResultError(label, `Semantic Scholar returned 404 for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'Semantic Scholar throttles anonymous traffic; set SEMANTIC_SCHOLAR_API_KEY (free at https://www.semanticscholar.org/product/api) or wait a minute and retry.',
        );
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
    }
    let body;
    try {
        body = await resp.json();
    } catch (err) {
        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
    }
    if (body && typeof body === 'object' && body.error) {
        throw new CommandExecutionError(`${label} returned an error: ${body.error}`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Validate the reference: re-run `opencli semanticscholar search "<title>"` and use the paperId from results.
  2. Double-check the id for typos and that it is currently valid (try the DOI or arXiv form instead).
  3. If using a typed prefix, confirm the id under that prefix exists (e.g. PMID ids must exist in PubMed).
  4. In scripts, catch EmptyResultError and skip/log the id instead of aborting.

Example fix

// before
opencli semanticscholar paper "10.9999/nonexistent-doi"
// after
opencli semanticscholar search "Attention Is All You Need"
opencli semanticscholar paper "ARXIV:1706.03762"
Defensive patterns

Strategy: try-catch

Validate before calling

// validate ref format before calling (avoids most 404s)
if (!isRecognizedPaperRef(ref)) throw new Error(`unrecognized paper ref: ${ref}`);

Try / catch

try {
    const paper = await fetchPaper(ref);
} catch (err) {
    if (err instanceof EmptyResultError) {
        console.warn(`Not found on Semantic Scholar: ${ref}; skipping.`);
        return null;
    }
    throw err;
}

Prevention

When it happens

Trigger: Calling `paper`/`citations`/`recommendations` with an id the API cannot resolve: a typo in a 40-hex paperId, a deleted/merged paper, an ARXIV:/DOI:/PMID: id that Semantic Scholar has no record for, or a CorpusId that does not exist.

Common situations: Hardcoded ids copied from another database (MAG ids retired after MAG shutdown); stale cached paperIds; DOIs of papers never ingested by Semantic Scholar; scripts feeding ids from an outdated export.

Related errors


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