{"record":{"id":"e02a5d021690a477","repo":"jackwener/OpenCLI","slug":"chess-com-game-archive-entry-does-not-include-the","errorCode":null,"errorMessage":"Chess.com game archive entry does not include the requested player","messagePattern":"Chess\\.com game archive entry does not include the requested player","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/chess/utils.js","lineNumber":138,"sourceCode":"    if (!isPlainObject(game)) {\n        throw new CommandExecutionError('Chess.com game archive entry is not an object');\n    }\n    if (typeof game.url !== 'string' || !/^https:\\/\\/www\\.chess\\.com\\/game\\/(?:live|daily)\\/\\d+(?:$|[/?#])/i.test(game.url)) {\n        throw new CommandExecutionError('Chess.com game archive entry is missing a stable game URL');\n    }\n    const white = game?.white || {};\n    const black = game?.black || {};\n    if (!isPlainObject(white) || !isPlainObject(black)) {\n        throw new CommandExecutionError('Chess.com game archive entry has malformed player objects');\n    }\n    if (typeof white.username !== 'string' || !white.username.trim() || typeof black.username !== 'string' || !black.username.trim()) {\n        throw new CommandExecutionError('Chess.com game archive entry is missing stable player identities');\n    }\n    const viewerLower = String(viewerUsername || '').toLowerCase();\n    const viewerIsWhite = String(white.username || '').toLowerCase() === viewerLower;\n    const viewerIsBlack = String(black.username || '').toLowerCase() === viewerLower;\n    if (!viewerIsWhite && !viewerIsBlack) {\n        throw new CommandExecutionError('Chess.com game archive entry does not include the requested player');\n    }\n    const me = viewerIsWhite ? white : black;\n    const opp = viewerIsWhite ? black : white;\n    const eco = game?.eco || '';\n    return {\n        date: formatDate(game?.end_time),\n        time_class: game?.time_class || '',\n        rated: game?.rated === true,\n        my_color: viewerIsWhite ? 'white' : 'black',\n        my_rating: me?.rating ?? '',\n        my_result: me?.result || '',\n        opponent: opp?.username || '',\n        opponent_rating: opp?.rating ?? '',\n        accuracy_white: typeof game?.accuracies?.white === 'number' ? game.accuracies.white : '',\n        accuracy_black: typeof game?.accuracies?.black === 'number' ? game.accuracies.black : '',\n        eco,\n        opening_name: openingName(eco),\n        url: game?.url || '',","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/chess/utils.js#L120-L156","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst rows = archive.games.map((g) => mapGameRow(g, opponentName)); // wrong viewer\n// after\nconst rows = archive.games\n  .filter((g) => [g.white?.username, g.black?.username].some((u) => String(u).toLowerCase() === user.toLowerCase()))\n  .map((g) => mapGameRow(g, user));","handlingStrategy":"validation","validationCode":"const viewer = String(viewerUsername || '').toLowerCase();\nconst involved = [game?.white?.username, game?.black?.username]\n  .some((u) => typeof u === 'string' && u.toLowerCase() === viewer);\nif (!involved) {\n  throw new Error(`game does not involve ${viewerUsername}; check archive URL and viewer arg`);\n}","typeGuard":"function involvesPlayer(g, username) {\n  const v = String(username || '').toLowerCase();\n  return [g?.white?.username, g?.black?.username]\n    .some((u) => typeof u === 'string' && u.toLowerCase() === v);\n}\n// usage: games.filter((g) => involvesPlayer(g, user)).map((g) => mapGameRow(g, user));","tryCatchPattern":"try {\n  row = mapGameRow(game, user);\n} catch (err) {\n  if (String(err.message).includes('does not include the requested player')) {\n    console.warn(`Skipping game between ${game?.white?.username} and ${game?.black?.username}: viewer mismatch`);\n  } else { throw err; }\n}","preventionTips":["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"],"tags":["api","games","chess-com","data-mismatch"],"backgroundTag":"record-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}