{"record":{"id":"d8977fe87f3907d9","repo":"jackwener/OpenCLI","slug":"chess-com-stats-payload-for-kind-best-is-not-an","errorCode":null,"errorMessage":"Chess.com stats payload for ${kind}.best is not an object","messagePattern":"Chess\\.com stats payload for (.+?)\\.best is not an object","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/chess/utils.js","lineNumber":79,"sourceCode":"    }\n    if (!isPlainObject(payload)) {\n        throw new CommandExecutionError(`Chess.com API returned an unexpected payload shape for ${url}`);\n    }\n    return payload;\n}\n\n/** Pull rating + record fields out of a stats sub-object (`chess_rapid` etc). */\nexport function summarizeStats(stats, kind) {\n    const k = stats?.[kind];\n    if (!k) return null;\n    if (!isPlainObject(k)) {\n        throw new CommandExecutionError(`Chess.com stats payload for ${kind} is not an object`);\n    }\n    if (!isOptionalPlainObject(k.last)) {\n        throw new CommandExecutionError(`Chess.com stats payload for ${kind}.last is not an object`);\n    }\n    if (!isOptionalPlainObject(k.best)) {\n        throw new CommandExecutionError(`Chess.com stats payload for ${kind}.best is not an object`);\n    }\n    if (!isOptionalPlainObject(k.record)) {\n        throw new CommandExecutionError(`Chess.com stats payload for ${kind}.record is not an object`);\n    }\n    const record = isPlainObject(k.record) ? k.record : {};\n    return {\n        kind: kind.replace(/^chess_/, ''),\n        rating_current: k.last?.rating ?? '',\n        rating_best: k.best?.rating ?? '',\n        wins: record.win ?? '',\n        losses: record.loss ?? '',\n        draws: record.draw ?? '',\n    };\n}\n\n/** Parse an end_time epoch (seconds) into YYYY-MM-DD. */\nexport function formatDate(epochSeconds) {\n    if (!epochSeconds || typeof epochSeconds !== 'number') return '';","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/chess/utils.js#L61-L97","documentation":"summarizeStats() validates that k.best (best rating snapshot) is either absent/null or a plain object. This throw fires when k.best is present but some other type (string, number, array). It guards the later k.best?.rating lookup so a corrupt field cannot silently yield wrong output.","triggerScenarios":"stats[kind].best in the payload fetched by rows()/row() is a truthy non-object, e.g. best: 1620 or best: \"1620\".","commonSituations":"A normalization step replaced best with the numeric peak rating; stale cache from an older tool version that flattened the field; hand-written mock data; Chess.com schema drift for the stats endpoint.","solutions":["Re-fetch the stats payload directly from https://api.chess.com/pub/player/{username}/stats to get the documented shape","Find and fix the intermediate transform that converted best from an object into a scalar","In mocks/fixtures use best: { rating: 1620, date: 1700000000 }","If the schema changed upstream, update summarizeStats accordingly"],"exampleFix":"// before\nstats.chess_rapid.best = 1620;\n// after\nstats.chess_rapid.best = { rating: 1620, date: Math.floor(Date.now() / 1000) };","handlingStrategy":"validation","validationCode":"const best = stats?.[kind]?.best;\nif (best != null && (typeof best !== 'object' || Array.isArray(best))) {\n  throw new Error(`${kind}.best must be an object like { rating, date }`);\n}","typeGuard":"function hasValidBest(k) {\n  return k?.best == null ||\n    (typeof k.best === 'object' && !Array.isArray(k.best) && typeof k.best.rating === 'number');\n}","tryCatchPattern":"try {\n  const row = summarizeStats(stats, 'chess_blitz');\n} catch (err) {\n  if (String(err.message).includes('.best is not an object')) {\n    console.warn('best section malformed; ignoring best-rating column for this kind');\n  } else { throw err; }\n}","preventionTips":["Store best as an object with rating/date; never replace it with the numeric peak","Validate cached payloads on write and on read","Keep fixtures in the exact pub API shape","Re-fetch fresh stats when validation fails rather than patching in place"],"tags":["api","schema-validation","chess-com","stats"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}