jackwener/OpenCLI · error · CommandExecutionError
Zhihu answer comments contained a malformed ${role} row
Error message
Zhihu answer comments contained a malformed ${role} row What it means
CommandExecutionError thrown by describeComment when a comment row (root or child, per the role argument) is null, not an object, or an Array. The library validates each row from payload.data before extracting its id and reply relationships, so downstream code can trust the descriptor shape.
Source
Thrown at clis/zhihu/answer-comments-helpers.js:91
}
if (status === 401 || status === 403 || code === '40353' || payload.__needLogin) {
throw new AuthRequiredError('www.zhihu.com', `Failed to fetch Zhihu ${label}`);
}
if (status === 404 && notFoundDetail) throw new EmptyResultError('zhihu answer-comments', notFoundDetail);
if (status >= 400) throw new CommandExecutionError(`Zhihu ${label} request failed (HTTP ${status})`);
throw new CommandExecutionError(`Zhihu ${label} returned an error payload: ${payload.__errorMessage || code}`);
}
if (!Array.isArray(payload.data) || !payload.paging || typeof payload.paging !== 'object') {
throw new CommandExecutionError(`Zhihu ${label} returned a malformed payload`);
}
if (typeof payload.paging.is_end !== 'boolean') {
throw new CommandExecutionError(`Zhihu ${label} returned malformed paging state`);
}
return payload;
}
function describeComment(comment, role, expectedRootId = '') {
if (!comment || typeof comment !== 'object' || Array.isArray(comment)) {
throw new CommandExecutionError(`Zhihu answer comments contained a malformed ${role} row`);
}
const id = commentId(comment.id);
if (!id) throw new CommandExecutionError(`Zhihu answer comments contained a ${role} row without a stable id`);
const rootId = role === 'root' ? id : commentId(comment.reply_root_comment_id);
const parentId = role === 'root' ? '' : commentId(comment.reply_comment_id);
if (role === 'child' && rootId !== expectedRootId) {
throw new CommandExecutionError(`Zhihu answer comment ${id} had conflicting root provenance`);
}
if (role === 'child' && !parentId) {
throw new CommandExecutionError(`Zhihu answer comment ${id} did not identify its immediate parent`);
}
const signature = JSON.stringify({
role,
rootId,
parentId,
author: memberName(comment.author),
replyTo: role === 'child' ? memberName(comment.reply_to_author) : '',
content: stripHtml(comment.content || ''),View on GitHub (pinned to 49907e53dc)
Solutions
- Filter out non-object rows before processing: data.filter(c => c && typeof c === 'object' && !Array.isArray(c))
- Handle deleted comments (null rows) by skipping them rather than failing the whole fetch
- Log the offending row with -v to check for API schema changes
- Update describeComment/parsing if Zhihu changed the row shape
Example fix
// before payload.data.forEach(c => describeComment(c, 'root')); // throws on null placeholder rows // after payload.data.filter(c => c && typeof c === 'object' && !Array.isArray(c)) .forEach(c => describeComment(c, 'root'));
Defensive patterns
Strategy: type-guard
Validate before calling
const rows = (payload.data || []).filter(c => c && typeof c === 'object' && !Array.isArray(c));
Type guard
function isCommentRow(c){ return !!c && typeof c === 'object' && !Array.isArray(c) && typeof c.id !== 'undefined'; } Try / catch
try { return describeComment(row, role, rootId); } catch (e) { if (/malformed .* row/.test(e.message)) { log(`skipping bad row: ${JSON.stringify(row)}`); return null; } throw e; } Prevention
- Filter null/placeholder rows before describing
- Skip-and-log bad rows in batch processing instead of failing the run
- Assert row schema in tests against a captured payload
- Check for API schema changes when many rows suddenly fail validation
When it happens
Trigger: An entry in payload.data (or a nested child in reply list) is null, a primitive, or an array — e.g. Zhihu inserting null placeholders for deleted comments, or a schema change altering row structure.
Common situations: Deleted/removed comments serialized as null in the data array; API returning flattened rows after a schema change; children embedded differently than expected.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Bilibili creator comparison returned a malformed manuscript
- returned malformed post row
- Nowcoder detail returned mismatched moment entity ids
- Suno generation returned malformed clip identity.
- Malformed Twitter following user: missing screen_name
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/5b87f5ae791284a3.
Report an issue: GitHub.