{"record":{"id":"760880af63e72202","repo":"jackwener/OpenCLI","slug":"semanticscholar-paper-returned-an-unexpected-paylo","errorCode":null,"errorMessage":"semanticscholar paper returned an unexpected payload shape","messagePattern":"semanticscholar paper returned an unexpected payload shape","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/semanticscholar/paper.js","lineNumber":41,"sourceCode":"cli({\n    site: 'semanticscholar',\n    name: 'paper',\n    access: 'read',\n    description: 'Semantic Scholar paper detail (citation graph + AI tldr) by paperId, DOI, or arXiv id',\n    domain: 'api.semanticscholar.org',\n    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { name: 'id', positional: true, required: true, help: 'paperId (40-char hex), DOI, arXiv id, or prefixed id (e.g. \"ARXIV:1706.03762\", \"PMID:12345\")' },\n    ],\n    columns: ['paperId', 'doi', 'title', 'year', 'firstAuthor', 'citationCount', 'influentialCitationCount', 'referenceCount', 'tldr', 'url'],\n    func: async (args) => {\n        const ref = requirePaperRef(args.id);\n        const url = `${S2_GRAPH_BASE}/paper/${encodeURIComponent(ref)}?fields=${FIELDS}`;\n        const body = await s2Fetch(url, 'semanticscholar paper');\n\n        if (!body || typeof body !== 'object') {\n            throw new CommandExecutionError('semanticscholar paper returned an unexpected payload shape');\n        }\n        const row = normalizePaperRow(body, 'paper');\n\n        return [{\n            ...row,\n            influentialCitationCount: optionalNumber(body.influentialCitationCount, 'paper influentialCitationCount'),\n            referenceCount: optionalNumber(body.referenceCount, 'paper referenceCount'),\n            tldr: tldrText(body.tldr),\n        }];\n    },\n});\n","sourceCodeStart":23,"sourceCodeEnd":53,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/semanticscholar/paper.js#L23-L53","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the raw body (curl the URL) to see what came back instead of a paper object.","Retry after a pause — S2 soft errors with 200 are usually throttling-related and transient.","Verify the paper reference is valid with a fresh lookup (try paperId, DOI, or arXiv forms).","Check the Semantic Scholar API changelog for response format changes and update the client.","Bypass any proxy to rule out an intermediary substituting the body."],"exampleFix":"// before\nconst body = await s2Fetch(url, 'semanticscholar paper');\nconst row = normalizePaperRow(body, 'paper');\n// after\nconst body = await s2Fetch(url, 'semanticscholar paper');\nif (!body || typeof body !== 'object' || Array.isArray(body) || body.error) {\n  throw new Error(`S2 paper lookup failed: ${JSON.stringify(body).slice(0, 200)}`);\n}\nconst row = normalizePaperRow(body, 'paper');","handlingStrategy":"type-guard","validationCode":"function isPaperObject(body) {\n  return body !== null && typeof body === 'object' && !Array.isArray(body) && typeof body.paperId === 'string';\n}","typeGuard":"function isPaperRow(body) {\n  return typeof body === 'object' && body !== null && !Array.isArray(body)\n    && 'paperId' in body && 'title' in body;\n}","tryCatchPattern":"try {\n  paper = await cli('semanticscholar paper', id);\n} catch (err) {\n  if (/unexpected payload shape/.test(err.message)) {\n    await sleep(1500);\n    paper = await cli('semanticscholar paper', id); // throttling soft errors are transient\n  } else throw err;\n}","preventionTips":["Retry with backoff — S2 serves soft error bodies with HTTP 200 under load.","Verify paper refs resolve before batch runs (one cheap probe call).","Log raw bodies on shape failures to distinguish throttling from schema drift.","Subscribe to Semantic Scholar API status/changelog updates."],"tags":["api","schema","semanticscholar","response-shape"],"backgroundTag":"unexpected-api-payload-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}