jackwener/OpenCLI · error · CommandExecutionError

Chess.com game archive entry is missing stable player identi

Error message

Chess.com game archive entry is missing stable player identities

What it means

mapGameRow() requires a non-empty username string for both the white and black players, since identities anchor the row and the viewer-orientation logic. This throw fires when either side's username is missing, not a string, or an empty/whitespace-only string, even though the enclosing player object itself was a plain object.

Source

Thrown at clis/chess/utils.js:132

/**
 * Map a Chess.com game record (from the monthly archive) to a flat row.
 * The viewer perspective controls win/loss orientation.
 */
export function mapGameRow(game, viewerUsername) {
    if (!isPlainObject(game)) {
        throw new CommandExecutionError('Chess.com game archive entry is not an object');
    }
    if (typeof game.url !== 'string' || !/^https:\/\/www\.chess\.com\/game\/(?:live|daily)\/\d+(?:$|[/?#])/i.test(game.url)) {
        throw new CommandExecutionError('Chess.com game archive entry is missing a stable game URL');
    }
    const white = game?.white || {};
    const black = game?.black || {};
    if (!isPlainObject(white) || !isPlainObject(black)) {
        throw new CommandExecutionError('Chess.com game archive entry has malformed player objects');
    }
    if (typeof white.username !== 'string' || !white.username.trim() || typeof black.username !== 'string' || !black.username.trim()) {
        throw new CommandExecutionError('Chess.com game archive entry is missing stable player identities');
    }
    const viewerLower = String(viewerUsername || '').toLowerCase();
    const viewerIsWhite = String(white.username || '').toLowerCase() === viewerLower;
    const viewerIsBlack = String(black.username || '').toLowerCase() === viewerLower;
    if (!viewerIsWhite && !viewerIsBlack) {
        throw new CommandExecutionError('Chess.com game archive entry does not include the requested player');
    }
    const me = viewerIsWhite ? white : black;
    const opp = viewerIsWhite ? black : white;
    const eco = game?.eco || '';
    return {
        date: formatDate(game?.end_time),
        time_class: game?.time_class || '',
        rated: game?.rated === true,
        my_color: viewerIsWhite ? 'white' : 'black',
        my_rating: me?.rating ?? '',
        my_result: me?.result || '',
        opponent: opp?.username || '',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-fetch the archive entry from the pub API, which always includes usernames for completed games
  2. Fix the normalization step that strips or renames username
  3. Validate entries before mapping: drop any where typeof g.white?.username !== 'string' or the value is blank
  4. Correct fixtures to include non-empty username strings on both sides

Example fix

// before
const game = { url, white: { rating: 1500 }, black: { username: 'magnus' } };
// after
const game = { url, white: { username: 'hikaru', rating: 1500 }, black: { username: 'magnus', rating: 2850 } };
Defensive patterns

Strategy: validation

Validate before calling

for (const side of ['white', 'black']) {
  const u = game?.[side]?.username;
  if (typeof u !== 'string' || !u.trim()) {
    throw new Error(`game.${side}.username must be a non-empty string`);
  }
}

Type guard

function hasPlayerIdentities(g) {
  const ok = (p) => typeof p?.username === 'string' && p.username.trim().length > 0;
  return ok(g?.white) && ok(g?.black);
}

Try / catch

try {
  row = mapGameRow(game, user);
} catch (err) {
  if (String(err.message).includes('missing stable player identities')) {
    console.warn('Skipping entry: player username missing or blank');
  } else { throw err; }
}

Prevention

When it happens

Trigger: Archive entry with white.username or black.username absent, empty (''), whitespace-only, or a non-string (number/undefined) when mapping rows for a player via row().

Common situations: Fixtures with partially filled player objects; a data pipeline that dropped username fields during normalization; very old or corrupt archive entries; callers passing game data from a custom endpoint that omits usernames for anonymous guests.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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