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
- Re-fetch the archive entry from the pub API, which always includes usernames for completed games
- Fix the normalization step that strips or renames username
- Validate entries before mapping: drop any where typeof g.white?.username !== 'string' or the value is blank
- 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
- Never strip or rename the username field during normalization
- Ensure both sides have non-empty username strings in fixtures
- Filter entries lacking identities before mapping rows
- Prefer fresh pub archive data over hand-transformed copies
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
- Chess.com game archive entry is not an object
- Chess.com game archive entry has malformed player objects
- Chess.com stats payload for ${kind} is not an object
- Chess.com stats payload for ${kind}.last is not an object
- Chess.com stats payload for ${kind}.best is not an object
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/cb95780b693071fa.
Report an issue: GitHub.