jackwener/OpenCLI · warning · EmptyResultError

Chess.com returned 404 for ${url}

Error message

Chess.com returned 404 for ${url}

What it means

An EmptyResultError thrown when the Chess.com API responds with HTTP 404 for the requested URL. The library deliberately maps 404 to the empty-result family: the endpoint exists but no resource was found for that player/game/month, which callers usually want to treat as 'no data' rather than a hard failure.

Source

Thrown at clis/chess/utils.js:54

            `Invalid Chess.com game URL: "${value}"`,
            'Expected https://www.chess.com/game/live/<id> or https://www.chess.com/game/daily/<id>.',
        );
    }
    return { kind: m[1].toLowerCase(), id: m[2] };
}

export async function chessApi(path, fetchImpl = fetch) {
    const url = path.startsWith('http') ? path : `${API_BASE}${path}`;
    let resp;
    try {
        resp = await fetchImpl(url, { headers: { 'User-Agent': UA, accept: 'application/json' } });
    } catch (error) {
        throw new CommandExecutionError(`Failed to fetch Chess.com API ${url}: ${error?.message || error}`);
    }
    if (!resp || typeof resp !== 'object') {
        throw new CommandExecutionError(`Chess.com API returned an invalid response object for ${url}`);
    }
    if (resp.status === 404) throw new EmptyResultError(`Chess.com returned 404 for ${url}`);
    if (!resp.ok) throw new CommandExecutionError(`Chess.com API returned HTTP ${resp.status} for ${url}`);
    let payload;
    try {
        payload = await resp.json();
    } catch (error) {
        throw new CommandExecutionError(`Chess.com API returned malformed JSON for ${url}: ${error?.message || error}`);
    }
    if (!isPlainObject(payload)) {
        throw new CommandExecutionError(`Chess.com API returned an unexpected payload shape for ${url}`);
    }
    return payload;
}

/** Pull rating + record fields out of a stats sub-object (`chess_rapid` etc). */
export function summarizeStats(stats, kind) {
    const k = stats?.[kind];
    if (!k) return null;
    if (!isPlainObject(k)) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Double-check the username spelling (note validateUsername lowercases it first).
  2. Confirm the player/game exists on chess.com in a browser.
  3. Catch EmptyResultError in the caller and render a 'not found' message instead of failing.
  4. For archives, derive valid YYYY/MM ranges from the player's joined date.

Example fix

// before
const rows = await chessStats({ username: 'hikarru' }); // typo -> 404
// after
try {
  const rows = await chessStats({ username: 'hikaru' });
} catch (e) {
  if (e.name === 'EmptyResultError') return console.log('Player not found');
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check existence with the profile endpoint:
const profile = await chessApi(`/player/${encodeURIComponent(username)}`); // 404 here means user does not exist

Type guard

function isEmptyResult(e) {
  return e instanceof Error && e.name === 'EmptyResultError' && /returned 404/.test(e.message);
}

Try / catch

try {
  const rows = await chessStats({ username });
} catch (e) {
  if (e.name === 'EmptyResultError') {
    return console.log(`No Chess.com data found for ${username}.`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Querying /player/<username>/stats or monthly archives for a username that does not exist, a game id that does not exist, or a YYYY/MM archive month with no games for an existing player.

Common situations: Typo in the username; querying a player who closed/renamed their account; requesting a month before the account existed; stale game id from an old link.

Related errors


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