jackwener/OpenCLI · error · CommandExecutionError
Chess.com game archive entry does not include the requested
Error message
Chess.com game archive entry does not include the requested player
What it means
mapGameRow() orients win/loss columns from the requested viewer's perspective, so the archive entry must contain the requested player on one of the sides. This throw fires when neither white.username nor black.username matches viewerUsername case-insensitively — meaning the game genuinely does not involve the requested account (wrong archive month, mistyped username, or mismatched case/alias).
Source
Thrown at clis/chess/utils.js:138
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 || '',
opponent_rating: opp?.rating ?? '',
accuracy_white: typeof game?.accuracies?.white === 'number' ? game.accuracies.white : '',
accuracy_black: typeof game?.accuracies?.black === 'number' ? game.accuracies.black : '',
eco,
opening_name: openingName(eco),
url: game?.url || '',View on GitHub (pinned to 49907e53dc)
Solutions
- Check that viewerUsername is passed and matches (case-insensitively) a player in each game — log both usernames on failure
- Verify the archive URL uses the same username as viewerUsername: /pub/player/{viewerUsername}/games/{yyyy}/{mm}
- If the account was renamed, fetch archives under the username that owned the games at the time or resolve the current handle first
- Filter archive entries to those containing the viewer before mapping instead of letting the mapper throw
Example fix
// before const rows = archive.games.map((g) => mapGameRow(g, opponentName)); // wrong viewer // after const rows = archive.games .filter((g) => [g.white?.username, g.black?.username].some((u) => String(u).toLowerCase() === user.toLowerCase())) .map((g) => mapGameRow(g, user));
Defensive patterns
Strategy: validation
Validate before calling
const viewer = String(viewerUsername || '').toLowerCase();
const involved = [game?.white?.username, game?.black?.username]
.some((u) => typeof u === 'string' && u.toLowerCase() === viewer);
if (!involved) {
throw new Error(`game does not involve ${viewerUsername}; check archive URL and viewer arg`);
} Type guard
function involvesPlayer(g, username) {
const v = String(username || '').toLowerCase();
return [g?.white?.username, g?.black?.username]
.some((u) => typeof u === 'string' && u.toLowerCase() === v);
}
// usage: games.filter((g) => involvesPlayer(g, user)).map((g) => mapGameRow(g, user)); Try / catch
try {
row = mapGameRow(game, user);
} catch (err) {
if (String(err.message).includes('does not include the requested player')) {
console.warn(`Skipping game between ${game?.white?.username} and ${game?.black?.username}: viewer mismatch`);
} else { throw err; }
} Prevention
- Always pass the same username used in the archive URL as viewerUsername
- Never leave viewerUsername empty/undefined when mapping archive rows
- If the account was renamed, resolve the owning handle before fetching archives
- Filter games to those involving the viewer before calling mapGameRow
When it happens
Trigger: Calling row()/archive listing for username A but the fetched month archive contains games of username B (wrong URL path), or the entry's usernames differ from the requested handle (renamed account, different capitalization is fine but a different name is not), or viewerUsername was omitted/empty so it matches neither side.
Common situations: Passing an empty or wrong viewerUsername argument while iterating another player's archive; requesting the wrong month URL so games returned belong to a different account; account renamed after games were played (old username in archives vs new handle requested); using the opponent's username by mistake.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Chess.com game archive entry is not an object
- Chess.com game archive entry is missing a stable game URL
- Chess.com game archive entry has malformed player objects
- Chess.com game archive entry is missing stable player identi
- Chess.com API returned an unexpected payload shape for ${url
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e02a5d021690a477.
Report an issue: GitHub.