{"record":{"id":"ba569f9e44aa2ea4","repo":"jackwener/OpenCLI","slug":"commandname-malformed-comment-row-field","errorCode":null,"errorMessage":"${commandName}: malformed comment row ${field}","messagePattern":"(.+?): malformed comment row (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/comments.js","lineNumber":71,"sourceCode":"        return 0;\n    if (integerRe.test(raw))\n        return Number(raw.replace(/[,+，]/g, ''));\n    const short = raw.match(shortformRe);\n    if (!short)\n        return 0;\n    const numeric = Number(short[1].replace(/[,，]/g, ''));\n    if (!Number.isFinite(numeric))\n        return 0;\n    const unit = short[2].toLowerCase();\n    const multiplier = unit === 'w' || unit === '万' ? 10000 : 1000;\n    return Math.round(numeric * multiplier);\n}\n\nfunction normalizeOptionalString(value, field, commandName) {\n    if (value == null)\n        return '';\n    if (typeof value !== 'string') {\n        throw new CommandExecutionError(`${commandName}: malformed comment row ${field}`);\n    }\n    return value;\n}\n\nexport function normalizeCommentImages(value, commandName) {\n    if (value == null)\n        return [];\n    if (!Array.isArray(value)) {\n        throw new CommandExecutionError(`${commandName}: malformed comment row images`);\n    }\n    const urls = [];\n    for (const raw of value) {\n        if (typeof raw !== 'string') {\n            throw new CommandExecutionError(`${commandName}: malformed comment row image URL`);\n        }\n        const trimmed = raw.trim();\n        let parsed;\n        try {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/comments.js#L53-L89","documentation":"normalizeOptionalString validates that a comment row field (e.g. text) coming from the page scrape is a string or null. Any non-string, non-null value means the comment row shape is unexpected, so a CommandExecutionError is thrown naming the field.","triggerScenarios":"row.text (or another field passed in) is a number, object, or array instead of a string, typically because the underlying page data structure changed.","commonSituations":"XHS comment payload format drift, a proxy/transform layer returning parsed objects instead of strings, or a caller passing raw JSON rows without validation.","solutions":["Log the offending comment row to inspect the actual field type.","Update the normalizer to coerce or handle the new shape (e.g. text as { content: ... } object).","Pre-validate scraped rows with a schema check before calling the comments API.","Pin/rollback to a library version matching the current XHS page format if this is a regression."],"exampleFix":"// before\nthrow new CommandExecutionError(`${commandName}: malformed comment row ${field}`);\n// after\nif (typeof value === 'number' || typeof value === 'bigint') return String(value);\nthrow new CommandExecutionError(`${commandName}: malformed comment row ${field}`);","handlingStrategy":"validation","validationCode":"if (row.text != null && typeof row.text !== 'string') {\n  throw new Error(`Unexpected text type: ${typeof row.text}`);\n}","typeGuard":"function isOptionalString(v) {\n  return v == null || typeof v === 'string';\n}","tryCatchPattern":"try {\n  rows = normalizeCommentRows(rawRows, cmd);\n} catch (e) {\n  if (e instanceof CommandExecutionError && /malformed comment row/.test(e.message)) {\n    console.error('Bad row:', JSON.stringify(rawRows).slice(0, 500));\n    rows = [];\n  } else throw e;\n}","preventionTips":["Log raw scraped rows so shape drift is visible in failures.","Coerce numeric-like text fields to strings before normalizing.","Pin library versions and test against live XHS after frontend deploys.","Validate rows with a lightweight schema before calling normalize."],"tags":["validation","scraping","schema"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}