jackwener/OpenCLI · error · CommandExecutionError

${commandName}: malformed comments payload

Error message

${commandName}: malformed comments payload

What it means

normalizeCommentRows is the entry normalizer for scraped comments. The whole payload must be an array (null/undefined is treated as empty). A non-array payload is rejected with CommandExecutionError as a malformed comments payload.

Source

Thrown at clis/xiaohongshu/comments.js:109

        }
        catch {
            throw new CommandExecutionError(`${commandName}: malformed comment row image URL`);
        }
        if ((parsed.protocol !== 'https:' && parsed.protocol !== 'http:') || parsed.username || parsed.password) {
            throw new CommandExecutionError(`${commandName}: malformed comment row image URL`);
        }
        const href = parsed.toString();
        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),

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Unwrap envelopes before calling: pass payload.comments, not the envelope object.
  2. Log the raw payload to see what shape is actually returned.
  3. Check that the comments container loaded (wait for the selector) before extraction.
  4. If the payload is legitimately absent, pass null/undefined instead of an empty object, which is accepted as [].

Example fix

// before
normalizeCommentRows(await fetchComments(page), cmd);
// after
const raw = await fetchComments(page);
normalizeCommentRows(Array.isArray(raw) ? raw : raw?.comments ?? null, cmd);
Defensive patterns

Strategy: type-guard

Validate before calling

const raw = await fetchComments(page);
if (raw != null && !Array.isArray(raw)) {
  throw new Error(`Comments payload is ${typeof raw}, expected array`);
}

Type guard

function isCommentPayload(v) {
  return v == null || (Array.isArray(v) && v.every(r => r && typeof r === 'object' && !Array.isArray(r)));
}

Try / catch

try {
  comments = normalizeCommentRows(raw, cmd);
} catch (e) {
  if (e instanceof CommandExecutionError && /malformed comments payload/.test(e.message)) {
    console.error('Payload shape:', typeof raw, raw && Object.keys(raw));
    comments = [];
  } else throw e;
}

Prevention

When it happens

Trigger: page.evaluate or an API wrapper returned an object/string/undefined instead of an array of comment rows.

Common situations: XHS comment section structure changed, the comment container selector matched nothing and the script returned null wrapped in an object, or the caller passed a { comments: [...] } envelope without unwrapping.

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/0acf918d0206511f. Report an issue: GitHub.