jackwener/OpenCLI · error · CommandExecutionError

semanticscholar citations row is missing citingPaper

Error message

semanticscholar citations row is missing citingPaper

What it means

The citations endpoint returns rows shaped `{ data: [{ citingPaper: {...} }] }`. Before unwrapping, each row is checked (clis/semanticscholar/citations.js:56): if an entry is null, not an object, or lacks a `citingPaper` key, this CommandExecutionError is thrown. It indicates the API violated its documented per-row contract.

Source

Thrown at clis/semanticscholar/citations.js:56

            throw new ArgumentError('semanticscholar citations offset must be a non-negative integer');
        }
        if (offset > 9999) {
            throw new ArgumentError('semanticscholar citations offset must be <= 9999');
        }
        const url = `${S2_GRAPH_BASE}/paper/${encodeURIComponent(ref)}/citations?fields=${FIELDS}&limit=${limit}&offset=${offset}`;
        const body = await s2Fetch(url, 'semanticscholar citations');

        const data = Array.isArray(body?.data) ? body.data : null;
        if (data === null) {
            throw new CommandExecutionError('semanticscholar citations returned an unexpected payload shape');
        }
        if (!data.length) {
            throw new EmptyResultError('semanticscholar citations', `No Semantic Scholar citations for "${args.id}" at offset ${offset}.`);
        }

        return data.slice(0, limit).map((entry, i) => {
            if (!entry || typeof entry !== 'object' || !('citingPaper' in entry)) {
                throw new CommandExecutionError('semanticscholar citations row is missing citingPaper');
            }
            return normalizePaperRow(entry.citingPaper, 'citations', { rank: offset + i + 1 });
        });
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Dump the raw response (curl the same URL) and inspect the offending row's shape.
  2. Retry — transient partial responses during S2 incidents usually resolve.
  3. Check the Semantic Scholar API docs/changelog for a citations response schema change and update the adapter.
  4. Filter or skip malformed rows client-side if you control the calling code.
  5. Report persistent occurrences with the paper id to Semantic Scholar support.

Example fix

// before
return data.map((entry) => normalizePaperRow(entry.citingPaper, 'citations'));
// after
return data
  .filter((entry) => entry && typeof entry === 'object' && 'citingPaper' in entry)
  .map((entry) => normalizePaperRow(entry.citingPaper, 'citations'));
Defensive patterns

Strategy: type-guard

Validate before calling

function rowHasCitingPaper(entry) {
  return Boolean(entry) && typeof entry === 'object' && 'citingPaper' in entry;
}
// filter before mapping:
const usable = data.filter(rowHasCitingPaper);

Type guard

function isCitationEntry(entry) {
  return typeof entry === 'object' && entry !== null
    && 'citingPaper' in entry
    && typeof entry.citingPaper === 'object'
    && entry.citingPaper !== null
    && typeof entry.citingPaper.title === 'string';
}

Try / catch

try {
  rows = await citations(id, offset);
} catch (err) {
  if (/missing citingPaper/.test(err.message)) {
    console.error('S2 citations row schema violation; inspecting raw payload...');
    // fetch raw body for diagnosis, retry once
    return citations(id, offset);
  }
  throw err;
}

Prevention

When it happens

Trigger: A row in body.data is null or a primitive; the API returns citations entries keyed differently (schema drift); partial/soft-error bodies that happen to include a top-level data array but not the citingPaper wrapper.

Common situations: Semantic Scholar A/B-testing or rolling out an API schema change; degraded/partial responses during incidents where rows come back null; a cached or proxied response with an older/newer shape.

Related errors


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