jackwener/OpenCLI · error · CommandExecutionError

${label} returned HTTP ${resp.status}

Error message

${label} returned HTTP ${resp.status}

What it means

lichessFetch wraps all Lichess API calls and throws CommandExecutionError when the HTTP response status is not ok. The 429 rate-limit case is handled separately with a richer message; this generic branch covers every other non-2xx status (404, 403, 5xx, etc.). It signals the Lichess endpoint rejected the request for a reason other than rate limiting.

Source

Thrown at clis/lichess/utils.js:79

        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });
    }
    catch (err) {
        throw new CommandExecutionError(
            `${label} request failed: ${err?.message ?? err}`,
            'Check that lichess.org is reachable from this network.',
        );
    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `Lichess returned 404 for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'Lichess throttles anonymous traffic at ~60 req/min; back off 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}`);
    }
    return body;
}

/** Format a lichess unix-ms timestamp as ISO date (YYYY-MM-DD). `null` when missing. */
export function formatTimestamp(ms) {
    if (typeof ms !== 'number' || !Number.isFinite(ms) || ms <= 0) return null;
    const d = new Date(ms);
    if (Number.isNaN(d.getTime())) return null;
    return d.toISOString();
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read resp.status in the error context and check Lichess API docs for that code (404 = not found, 403 = forbidden, 5xx = server side).
  2. Verify the username/ID/endpoint path passed to the command is correct.
  3. Check https://status.lichess.org for outages if the status is 5xx.
  4. Retry later if transient; if 403, obtain an OAuth token if the endpoint requires one.

Example fix

// before
const games = await lichessFetch('/api/user/definitely-not-a-user');
// after
let games;
try {
  games = await lichessFetch('/api/user/existing-user');
} catch (e) {
  if (String(e.message).includes('HTTP 404')) throw new EmptyResultError('no such lichess user');
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const m = /^lichess\b/i.test(cmd) && /\S+/.test(args[0] || '') ? args[0] : null;
if (!m) throw new Error('supply a valid lichess username/endpoint argument');

Type guard

function isOkStatus(status) { return Number.isInteger(status) && status >= 200 && status < 300; }

Try / catch

try {
  const data = await lichessFetch(path);
} catch (e) {
  const m = /HTTP (\d{3})/.exec(e.message);
  if (m) {
    const status = Number(m[1]);
    if (status >= 500 || status === 429) { /* retry with backoff */ }
    else throw new Error(`lichess request failed: ${status}`);
  } else throw e;
}

Prevention

When it happens

Trigger: Any lichessFetch call (via body and the lichess subcommands) where resp.ok is false and status !== 429 — e.g. requesting a non-existent user (404), hitting an endpoint that requires OAuth (403), or Lichess returning 5xx during incidents.

Common situations: Typing a wrong username in a lichess command; Lichess API outage or maintenance; calling an endpoint that now requires authentication after a Lichess API change; proxy/firewall injecting error responses.

Related errors


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