{"record":{"id":"b2b97960b2a42421","repo":"jackwener/OpenCLI","slug":"chess-com-stats-payload-for-kind-last-is-not-an","errorCode":null,"errorMessage":"Chess.com stats payload for ${kind}.last is not an object","messagePattern":"Chess\\.com stats payload for (.+?)\\.last is not an object","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/chess/utils.js","lineNumber":76,"sourceCode":"        payload = await resp.json();\n    } catch (error) {\n        throw new CommandExecutionError(`Chess.com API returned malformed JSON for ${url}: ${error?.message || error}`);\n    }\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","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/chess/utils.js#L58-L94","documentation":"Within summarizeStats(), the k.last field (most recent rating snapshot) may be absent, but if present it must be a plain object containing e.g. rating/date. This throw fires when k.last exists but is a non-object value (string, number, array). It is a strict shape check so downstream k.last?.rating access behaves predictably.","triggerScenarios":"Payload from /pub/player/{username}/stats where stats[kind].last is a non-object truthy value, e.g. last: \"1500\" or last: [..], passed via rows() or row().","commonSituations":"Transformed/normalized cache where last was flattened to a scalar rating; schema change on Chess.com; test fixture built with wrong nesting; a spreadsheet/CSV round-trip turned the nested object into a scalar.","solutions":["Re-fetch fresh stats from the pub API; the documented shape has last as an object with rating/date","Inspect the offending value (typeof k.last) and fix the producer that flattened it","If building fixtures/mocks, use last: { rating: 1500, date: 1700000000 }","Patch the mapping layer if Chess.com altered the last field's structure"],"exampleFix":"// before\nstats.chess_rapid.last = 1500; // flattened\n// after\nstats.chess_rapid.last = { rating: 1500, date: Math.floor(Date.now() / 1000) };","handlingStrategy":"validation","validationCode":"const last = stats?.[kind]?.last;\nif (last != null && (typeof last !== 'object' || Array.isArray(last))) {\n  throw new Error(`${kind}.last must be an object like { rating, date }`);\n}","typeGuard":"function hasValidLast(k) {\n  return k?.last == null ||\n    (typeof k.last === 'object' && !Array.isArray(k.last) && typeof k.last.rating === 'number');\n}","tryCatchPattern":"try {\n  const row = summarizeStats(stats, 'chess_rapid');\n} catch (err) {\n  if (String(err.message).includes('.last is not an object')) {\n    console.warn('last section malformed; refetching stats');\n    stats = await (await fetch(`${API_BASE}/player/${user}/stats`)).json();\n  } else { throw err; }\n}","preventionTips":["Keep last as { rating, date } objects; never flatten to a scalar rating","Re-fetch from the pub API instead of trusting old normalized caches","Validate shape when writing to cache, not only when reading","Check typeof before reassigning nested fields in pipelines"],"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-29T12:17:43.993Z"}