jackwener/OpenCLI · error · CommandExecutionError

${label} request failed: ${err?.message ?? err}

Error message

${label} request failed: ${err?.message ?? err}

What it means

s2Fetch wraps the underlying fetch call; when the request to api.semanticscholar.org throws at the network level (DNS failure, connection refused/reset, TLS error, timeout), it rethrows as CommandExecutionError prefixed with the request label and the original error message, advising to check network reachability. It converts low-level network exceptions into the CLI's uniform error type.

Source

Thrown at clis/semanticscholar/utils.js:99

}

/**
 * Fetch with optional `SEMANTIC_SCHOLAR_API_KEY` header. Retries once on 429
 * after a short pause; anonymous traffic hits the public ~100 req / 5 min
 * cap, and a single retry covers the typical burst-then-cool-down case.
 */
export async function s2Fetch(url, label) {
    const headers = { 'user-agent': UA, accept: 'application/json' };
    const apiKey = process.env.SEMANTIC_SCHOLAR_API_KEY;
    if (apiKey) headers['x-api-key'] = apiKey;

    let resp;
    let attempt = 0;
    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)`,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify reachability: `curl -I https://api.semanticscholar.org/graph/v1/paper/search?query=test`.
  2. Fix DNS or connect the network/VPN, then retry.
  3. Configure proxy environment variables (HTTPS_PROXY) and, for TLS interception, NODE_EXTRA_CA_CERTS with the corporate CA.
  4. In code, catch CommandExecutionError and retry with backoff for transient network errors.

Example fix

// before
opencli semanticscholar search "bert"   # fails: ENOTFOUND api.semanticscholar.org
// after
export HTTPS_PROXY=http://proxy.corp:8080
opencli semanticscholar search "bert"
Defensive patterns

Strategy: retry

Validate before calling

// preflight reachability check
const ok = await fetch('https://api.semanticscholar.org/graph/v1', { method: 'HEAD' })
    .then(r => r.ok || r.status < 500).catch(() => false);
if (!ok) console.error('api.semanticscholar.org unreachable; check network/proxy');

Try / catch

async function withNetworkRetry(fn, attempts = 3) {
    for (let i = 0; ; i++) {
        try { return await fn(); }
        catch (err) {
            const transient = /request failed|ENOTFOUND|ECONNREFUSED|ETIMEDOUT|ECONNRESET/i.test(String(err.message));
            if (!transient || i >= attempts - 1) throw err;
            await new Promise(r => setTimeout(r, 2 ** i * 1000));
        }
    }
}

Prevention

When it happens

Trigger: Any semanticscholar command (search, paper, citations, recommendations) executed while offline; DNS unable to resolve api.semanticscholar.org; firewall/proxy blocking outbound 443; TLS interception with an untrusted cert causing fetch to reject.

Common situations: Air-gapped CI runners; corporate proxies requiring configuration (HTTPS_PROXY / NODE_EXTRA_CA_CERTS); VPN not connected; transient ISP/DNS outages.

Related errors


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