jackwener/OpenCLI · error · CommandExecutionError

${commandName}: malformed comment row text

Error message

${commandName}: malformed comment row text

What it means

normalizeCommentRows validates each row returned by the in-page comments extractor. When a row is an object but its text field normalizes to empty/missing (normalizeOptionalString returns falsy), the command rejects it because a comment without text is useless downstream. The commandName ('xiaohongshu/comments') is interpolated into the message.

Source

Thrown at clis/xiaohongshu/comments.js:117

        if (!urls.includes(href))
            urls.push(href);
    }
    return urls;
}

export function normalizeCommentRows(value, commandName = 'xiaohongshu/comments') {
    if (value == null)
        return [];
    if (!Array.isArray(value)) {
        throw new CommandExecutionError(`${commandName}: malformed comments payload`);
    }
    return value.map((row, index) => {
        if (!row || typeof row !== 'object' || Array.isArray(row)) {
            throw new CommandExecutionError(`${commandName}: malformed comment row at index ${index}`);
        }
        const text = normalizeOptionalString(row.text, 'text', commandName);
        if (!text) {
            throw new CommandExecutionError(`${commandName}: malformed comment row text`);
        }
        const likes = Number(row.likes);
        if (!Number.isInteger(likes) || likes < 0) {
            throw new CommandExecutionError(`${commandName}: malformed comment row likes`);
        }
        if (typeof row.is_reply !== 'boolean') {
            throw new CommandExecutionError(`${commandName}: malformed comment row is_reply`);
        }
        return {
            author: normalizeOptionalString(row.author, 'author', commandName),
            authorHrefRaw: normalizeOptionalString(row.authorHrefRaw, 'authorHrefRaw', commandName),
            text,
            likes,
            time: normalizeOptionalString(row.time, 'time', commandName),
            is_reply: row.is_reply,
            reply_to: normalizeOptionalString(row.reply_to, 'reply_to', commandName),
            images: normalizeCommentImages(row.images, commandName),
        };

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect data.results from the extractor and drop rows with empty/whitespace text before normalizing
  2. Update buildCommentsExtractJs selectors to correctly capture comment text for the current Xiaohongshu DOM
  3. Filter malformed rows at the call site in normalizeCommentRows (skip instead of throw) if partial results are acceptable
  4. Retry the scrape — a transient render may have produced empty text nodes

Example fix

// before
const text = normalizeOptionalString(row.text, 'text', commandName);
if (!text) {
    throw new CommandExecutionError(`${commandName}: malformed comment row text`);
}
// after
const text = normalizeOptionalString(row.text, 'text', commandName);
if (!text) {
    return null; // filter out empty rows instead of failing the whole command
}
Defensive patterns

Strategy: validation

Validate before calling

function isValidCommentRows(rows) {
  return Array.isArray(rows) && rows.every(r => r && typeof r === 'object'
    && typeof r.text === 'string' && r.text.trim() !== '');
}
if (!isValidCommentRows(data.results)) throw new Error('extractor returned rows without text');

Type guard

const hasText = (row) => typeof row === 'object' && row !== null
  && typeof row.text === 'string' && row.text.trim().length > 0;

Try / catch

try {
  const comments = await cli.comments(noteUrl, { limit: 20 });
} catch (err) {
  if (err.message.includes('malformed comment row text')) {
    // retry or fall back to partial handling
  } else throw err;
}

Prevention

When it happens

Trigger: The page.evaluate extractor returned a results array containing a row object whose text is null, undefined, an empty string, or only whitespace (normalizeOptionalString trims).

Common situations: Xiaohongshu DOM changes so the extractor's text selector matches an empty node; comments deleted by the author leave empty placeholders; custom/patched extract JS passed via buildCommentsExtractJs emits rows without text.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/2861cda0e0c1e0c8. Report an issue: GitHub.