jackwener/OpenCLI · error · CommandExecutionError
${label} returned HTTP ${resp.status}
Error message
${label} returned HTTP ${resp.status} What it means
s2Fetch in clis/semanticscholar/utils.js wraps every Semantic Scholar API call. After handling 404 and 429 specially, any other non-2xx status falls through to this generic `CommandExecutionError` with the raw HTTP status code in the message. It signals the Semantic Scholar graph/recommendations API rejected the request for a reason not otherwise classified (e.g. 400 bad field list, 403 forbidden key, 5xx server outage).
Source
Thrown at clis/semanticscholar/utils.js:122
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}`);
}
return body;
}
/** Return the AI-generated one-line summary if present, else ''. */
export function tldrText(tldr) {
if (tldr && typeof tldr === 'object' && typeof tldr.text === 'string') {
return tldr.text.trim();
}View on GitHub (pinned to 49907e53dc)
Solutions
- Log or inspect resp.status in the message to identify the specific failure code.
- For 400: check the paper ref/fields in the URL; validate refs with requirePaperRef semantics (paperId, DOI, arXiv, or prefixed id).
- For 403: verify SEMANTIC_SCHOLAR_API_KEY is valid and not expired; request a free key at https://www.semanticscholar.org/product/api.
- For 5xx: wait and retry; check Semantic Scholar status; add backoff/retry around the command.
- Confirm network/proxy access to api.semanticscholar.org.
Example fix
// before (no key, hitting 403 on authenticated-only endpoint)
await s2Fetch(`${S2_GRAPH_BASE}/paper/${ref}?fields=title`, 'paper');
// after (attach key and handle non-ok status explicitly)
process.env.SEMANTIC_SCHOLAR_API_KEY = 'your-key';
try {
await s2Fetch(`${S2_GRAPH_BASE}/paper/${ref}?fields=title`, 'paper');
} catch (e) {
if (/HTTP 403/.test(e.message)) console.error('Check SEMANTIC_SCHOLAR_API_KEY');
throw e;
} Defensive patterns
Strategy: retry
Validate before calling
// Validate ref and key presence before calling
if (!/^(ARXIV|MAG|ACL|PMID|PMCID|URL|CorpusId|DBLP):|^10\.|^[0-9a-f]{40}$/i.test(ref) && !/^\d{4}\.\d{4,5}/.test(ref)) {
throw new Error(`Unrecognised paper ref: ${ref}`);
}
const hasKey = Boolean(process.env.SEMANTIC_SCHOLAR_API_KEY); Type guard
function isS2Ref(ref) {
return typeof ref === 'string' && (/^[0-9a-f]{40}$/i.test(ref.trim()) || /^(ARXIV|MAG|ACL|PMID|PMCID|URL|CorpusId|DBLP):/i.test(ref.trim()) || /^10\./.test(ref.trim()) || /^\d{4}\.\d{4,5}(v\d+)?$/.test(ref.trim()));
} Try / catch
try {
const data = await s2Fetch(url, 'paper');
} catch (err) {
const m = /HTTP (\d{3})/.exec(err.message);
if (m && +m[1] >= 500) return retryWithBackoff(() => s2Fetch(url, 'paper'), 3);
if (m && +m[1] === 403) console.error('Check SEMANTIC_SCHOLAR_API_KEY');
throw err;
} Prevention
- Set SEMANTIC_SCHOLAR_API_KEY to avoid anonymous throttling and 403s.
- Validate paper refs (DOI/arXiv/prefixed id) before building request URLs.
- Only request fields that the endpoint actually supports.
- Wrap calls with backoff for 5xx responses.
- Monitor Semantic Scholar status for ongoing outages.
When it happens
Trigger: Any s2Fetch call where api.semanticscholar.org responds with a status other than 404 or 429 and !resp.ok: malformed query params rejected with 400, invalid/expired SEMANTIC_SCHOLAR_API_KEY yielding 403, or upstream 500/502/503 outages.
Common situations: Requesting a field the API doesn't expose (400), a revoked API key (403), or transient Semantic Scholar server errors during partial outages; also hits when an unauthenticated client is IP-blocked (403) rather than merely throttled.
Related errors
- ${label} returned HTTP ${resp.status}: ${summarizeApiError(p
- HTTP ${result.httpStatus} from /api/organizations
- ${label} returned HTTP ${res.status}
- HTTP_ERROR
- HTTP_ERROR
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/8f9de2550096faf4.
Report an issue: GitHub.