jackwener/OpenCLI · error · CommandExecutionError
Zhihu answer comments contained a ${role} row without a stab
Error message
Zhihu answer comments contained a ${role} row without a stable id What it means
describeComment validates each comment row fetched from Zhihu's answer comments API. A row that is a valid object but whose comment.id cannot be normalized to a non-empty stable identifier (via commentId) is rejected. The library throws because every subsequent operation (dedup, tree building, provenance checks) keys off this id; a comment without one cannot be processed safely.
Source
Thrown at clis/zhihu/answer-comments-helpers.js:94
}
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 || ''),
});
return { id, rootId, parentId, signature };
}View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the offending row in the payload and confirm the comment.id field exists and normalizes to a non-empty value.
- Update this library to a version matching Zhihu's current comment_v5 API schema if the field name changed.
- Filter out rows with missing ids at the network/mock layer before they reach fetchPages, if you own the fetching boundary.
- Retry the fetch; if the API is returning a transient malformed page, a fresh request may contain valid rows.
Example fix
// before: mock fixture row without id
const rows = [{ author: { member: { name: 'alice' } }, content: 'hi' }];
// after
const rows = [{ id: '14839201', author: { member: { name: 'alice' } }, content: 'hi' }]; Defensive patterns
Strategy: validation
Validate before calling
// validate fixtures/responses before calling the API-driven code
function assertCommentRows(rows) {
for (const r of rows) {
if (r && typeof r === 'object' && !Array.isArray(r) && !String(r.id ?? '').trim()) {
throw new Error(`fixture row missing comment.id: ${JSON.stringify(r).slice(0, 80)}`);
}
}
} Type guard
function hasStableId(row) {
return !!row && typeof row === 'object' && !Array.isArray(row) &&
typeof row.id !== 'undefined' && row.id !== null && String(row.id).trim() !== '';
} Try / catch
try {
const comments = await fetchAnswerComments(page, answerId);
} catch (err) {
if (String(err.message).includes('without a stable id')) {
console.warn('Skipping page with malformed comment row (missing id)');
return [];
}
throw err;
} Prevention
- Keep API mocks regenerated from live captures so the id field is always present.
- Validate third-party payloads with a schema (zod/ajv) at the network boundary.
- Pin the library version known to match the current Zhihu comment_v5 schema and check for updates when the API drifts.
When it happens
Trigger: Zhihu's API returned a data row where comment.id is null, undefined, empty string, or a value commentId() normalizes to falsy (e.g. whitespace-only or non-numeric id) while the row is still an object.
Common situations: Zhihu schema drift (field renamed from id to id_string or similar), deleted/soft-removed comments appearing in a page payload, or proxied/cached responses with rows stripped of their id. Developers also hit it when mocking the API with fixtures missing the id field.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- Zhihu answer root comment ${id} had malformed child count
- Zhihu answer comment ${id} did not identify its immediate pa
- Zhihu search returned malformed result row identity
- Zhihu user answers returned malformed row identity
- Bilibili user search returned malformed result for ${input}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/4978472933be06a9.
Report an issue: GitHub.