jackwener/OpenCLI · error · CommandExecutionError

Stack Exchange API error ${json.error_id} (${json.error_name

Error message

Stack Exchange API error ${json.error_id} (${json.error_name}) for ${label}: ${json.error_message || ''}

What it means

This CommandExecutionError is thrown by fetchJson when the Stack Exchange API responds with HTTP 200 but the JSON body contains an error_id field, meaning the API itself rejected the request (e.g. invalid filter, throttled, or daily quota exhausted). The message includes the API's numeric error_id, symbolic error_name (e.g. throttle_violation, no_app), and error_message. It surfaces the API's own error contract rather than a transport failure.

Source

Thrown at clis/stackoverflow/read.js:60

    }
    if (!res.ok) {
        throw new CommandExecutionError(
            `Stack Exchange API HTTP ${res.status} for ${label}`,
            'Check the question id and quota (300/day per IP)',
        );
    }
    let json;
    try {
        json = await res.json();
    } catch (e) {
        const detail = e instanceof Error ? e.message : String(e);
        throw new CommandExecutionError(
            `Malformed JSON from Stack Exchange API for ${label}: ${detail}`,
            'The API returned a non-JSON body — likely a transient outage',
        );
    }
    if (json && json.error_id) {
        throw new CommandExecutionError(
            `Stack Exchange API error ${json.error_id} (${json.error_name}) for ${label}: ${json.error_message || ''}`,
            'Common causes: invalid filter, throttled, or quota exhausted',
        );
    }
    return json;
}

/**
 * CLI args may arrive as strings (`--limit 5` → `'5'`) when not coerced by the
 * arg type system. Coerce-then-validate so `Number.isInteger` actually catches
 * the bad cases, and reject NaN explicitly.
 */
function coerceInt(value) {
    if (value === undefined || value === null || value === '') return NaN;
    const n = typeof value === 'number' ? value : Number(value);
    return Number.isFinite(n) && Number.isInteger(n) ? n : NaN;
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Wait for the daily quota to reset or authenticate the request with an access_token/key to raise the quota
  2. Retry after a delay (backoff) if error_name is throttle_violation
  3. Check that the filter parameter (withbody) is valid against https://api.stackexchange.com/docs/filters
  4. Inspect json.error_message in the error text for the API's specific complaint and fix that parameter

Example fix

// before
const json = await fetchJson(url, label); // throws on error_id
// after
let json;
try {
    json = await fetchJson(url, label);
} catch (e) {
    if (/throttle_violation/.test(e.message)) {
        await new Promise((r) => setTimeout(r, 5000));
        json = await fetchJson(url, label);
    } else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

function isSeApiError(json) {
  return json != null && typeof json === 'object' && typeof json.error_id === 'number';
}

Try / catch

try {
  const data = await fetchJson(url, label);
} catch (e) {
  if (e instanceof CommandExecutionError && /throttle_violation/.test(e.message)) {
    // exponential backoff, then retry once
  } else if (/quota/.test(e.message)) {
    // surface a 'quota exhausted, resets in N hours' message; do not retry
  } else throw e;
}

Prevention

When it happens

Trigger: Any of the four fetchJson call sites (qData, answersData, qCommentsData, ansCommentsData, acceptedData) gets a 200 response whose body has error_id set — most commonly error 400 bad_parameter (invalid filter value), error 502 throttle_violation (too many requests per IP), or error 402 no_app when the 300/day anonymous IP quota is exhausted.

Common situations: Hitting the 300/day unauthenticated quota from a shared IP or CI runner; passing a --filter value the API rejects; rapid repeated invocations triggering per-IP throttling; API version (2.3) deprecating a parameter previously used.

Related errors


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