{"record":{"id":"a1288df800fdd147","repo":"jackwener/OpenCLI","slug":"commandname-malformed-comment-row-likes","errorCode":null,"errorMessage":"${commandName}: malformed comment row likes","messagePattern":"(.+?): malformed comment row likes","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/comments.js","lineNumber":121,"sourceCode":"}\n\nexport function normalizeCommentRows(value, commandName = 'xiaohongshu/comments') {\n    if (value == null)\n        return [];\n    if (!Array.isArray(value)) {\n        throw new CommandExecutionError(`${commandName}: malformed comments payload`);\n    }\n    return value.map((row, index) => {\n        if (!row || typeof row !== 'object' || Array.isArray(row)) {\n            throw new CommandExecutionError(`${commandName}: malformed comment row at index ${index}`);\n        }\n        const text = normalizeOptionalString(row.text, 'text', commandName);\n        if (!text) {\n            throw new CommandExecutionError(`${commandName}: malformed comment row text`);\n        }\n        const likes = Number(row.likes);\n        if (!Number.isInteger(likes) || likes < 0) {\n            throw new CommandExecutionError(`${commandName}: malformed comment row likes`);\n        }\n        if (typeof row.is_reply !== 'boolean') {\n            throw new CommandExecutionError(`${commandName}: malformed comment row is_reply`);\n        }\n        return {\n            author: normalizeOptionalString(row.author, 'author', commandName),\n            authorHrefRaw: normalizeOptionalString(row.authorHrefRaw, 'authorHrefRaw', commandName),\n            text,\n            likes,\n            time: normalizeOptionalString(row.time, 'time', commandName),\n            is_reply: row.is_reply,\n            reply_to: normalizeOptionalString(row.reply_to, 'reply_to', commandName),\n            images: normalizeCommentImages(row.images, commandName),\n        };\n    });\n}\n\n/**","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/comments.js#L103-L139","documentation":"normalizeCommentRows requires each comment row to carry a non-negative integer likes count. It coerces row.likes with Number() and throws if the result is not an integer or is negative. This guarantees the normalized row shape consumed by the CLI output.","triggerScenarios":"row.likes is undefined, null, a non-numeric string, a float, NaN, or a negative number such that Number(row.likes) fails the Number.isInteger/likes>=0 check.","commonSituations":"Xiaohongshu renders 'like' or '赞' instead of a number for zero-like comments so the extractor yields a non-numeric string; extractor grabs a formatted count like '1.2k'; DOM changes move the like-count element.","solutions":["Fix buildCommentsExtractJs to coerce like counts ('1.2k' -> 1200, missing -> 0) before returning rows","Pre-validate the extractor output: ensure every row.likes is a non-negative integer","Add a defensive default in normalizeCommentRows (treat missing likes as 0) if the shape permits","Retry the scrape if the count element had not finished rendering"],"exampleFix":"// before\nconst likes = Number(row.likes);\n// after\nconst rawLikes = String(row.likes ?? '').trim().toLowerCase();\nconst likes = /^\\d+(\\.\\d+)?k$/.test(rawLikes)\n    ? Math.round(parseFloat(rawLikes) * 1000)\n    : Number(rawLikes);","handlingStrategy":"validation","validationCode":"const toInt = v => { const n = Number(v); return Number.isInteger(n) && n >= 0 ? n : null; };\nif (!rows.every(r => toInt(r.likes) !== null)) throw new Error('row.likes must be a non-negative integer');","typeGuard":"const hasValidLikes = (row) => { const n = Number(row.likes); return Number.isInteger(n) && n >= 0; };","tryCatchPattern":"try {\n  const comments = await cli.comments(noteUrl);\n} catch (err) {\n  if (err.message.includes('malformed comment row likes')) {\n    // treat as extractor/DOM mismatch; retry or report\n  } else throw err;\n}","preventionTips":["Coerce formatted counts ('1.2k') to integers inside the extractor","Default missing like counts to 0 at extraction time","Validate extractor output shape in tests"],"tags":["validation","scraping","data-shape","type-coercion"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}