jackwener/OpenCLI · error · CommandExecutionError

semanticscholar paper returned an unexpected payload shape

Error message

semanticscholar paper returned an unexpected payload shape

What it means

The semanticscholar paper command (clis/semanticscholar/paper.js:41) expects `/paper/{ref}` to return a JSON object with the paper's fields. If the parsed body is null, an array, or otherwise not an object, this CommandExecutionError is thrown because the single-paper payload contract was violated.

Source

Thrown at clis/semanticscholar/paper.js:41

cli({
    site: 'semanticscholar',
    name: 'paper',
    access: 'read',
    description: 'Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id',
    domain: 'api.semanticscholar.org',
    strategy: Strategy.PUBLIC,
    browser: false,
    args: [
        { name: 'id', positional: true, required: true, help: 'paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. "ARXIV:1706.03762", "PMID:12345")' },
    ],
    columns: ['paperId', 'doi', 'title', 'year', 'firstAuthor', 'citationCount', 'influentialCitationCount', 'referenceCount', 'tldr', 'url'],
    func: async (args) => {
        const ref = requirePaperRef(args.id);
        const url = `${S2_GRAPH_BASE}/paper/${encodeURIComponent(ref)}?fields=${FIELDS}`;
        const body = await s2Fetch(url, 'semanticscholar paper');

        if (!body || typeof body !== 'object') {
            throw new CommandExecutionError('semanticscholar paper returned an unexpected payload shape');
        }
        const row = normalizePaperRow(body, 'paper');

        return [{
            ...row,
            influentialCitationCount: optionalNumber(body.influentialCitationCount, 'paper influentialCitationCount'),
            referenceCount: optionalNumber(body.referenceCount, 'paper referenceCount'),
            tldr: tldrText(body.tldr),
        }];
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the raw body (curl the URL) to see what came back instead of a paper object.
  2. Retry after a pause — S2 soft errors with 200 are usually throttling-related and transient.
  3. Verify the paper reference is valid with a fresh lookup (try paperId, DOI, or arXiv forms).
  4. Check the Semantic Scholar API changelog for response format changes and update the client.
  5. Bypass any proxy to rule out an intermediary substituting the body.

Example fix

// before
const body = await s2Fetch(url, 'semanticscholar paper');
const row = normalizePaperRow(body, 'paper');
// after
const body = await s2Fetch(url, 'semanticscholar paper');
if (!body || typeof body !== 'object' || Array.isArray(body) || body.error) {
  throw new Error(`S2 paper lookup failed: ${JSON.stringify(body).slice(0, 200)}`);
}
const row = normalizePaperRow(body, 'paper');
Defensive patterns

Strategy: type-guard

Validate before calling

function isPaperObject(body) {
  return body !== null && typeof body === 'object' && !Array.isArray(body) && typeof body.paperId === 'string';
}

Type guard

function isPaperRow(body) {
  return typeof body === 'object' && body !== null && !Array.isArray(body)
    && 'paperId' in body && 'title' in body;
}

Try / catch

try {
  paper = await cli('semanticscholar paper', id);
} catch (err) {
  if (/unexpected payload shape/.test(err.message)) {
    await sleep(1500);
    paper = await cli('semanticscholar paper', id); // throttling soft errors are transient
  } else throw err;
}

Prevention

When it happens

Trigger: The API returned 200 with a non-object JSON body — e.g. a JSON array, a bare string/number, null, or a soft-error envelope from Semantic Scholar served with HTTP 200 instead of the paper object.

Common situations: Semantic Scholar throttling or maintenance pages returned as JSON with 200; a proxy returning its own JSON body; an API response-shape change; requests redirected to an endpoint that answers with a different document type.

Related errors


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