jackwener/OpenCLI · error · CommandExecutionError
Zhihu answer comments reused id ${rootDescriptor.id} across
Error message
Zhihu answer comments reused id ${rootDescriptor.id} across graph roles What it means
buildCommentRows builds a strict tree: each comment id must appear exactly once across all root graphs (roots and replies). Before inserting a root comment it checks globalIds; a duplicate means the same comment id was returned as a root twice (or already seen as a reply), which would corrupt rank/reply assignment. The library throws to fail fast rather than emit duplicated rows.
Source
Thrown at clis/zhihu/answer-comments-helpers.js:241
parent_id: descriptor.parentId,
author: memberName(comment.author) || 'anonymous',
reply_to: descriptor.parentId ? memberName(comment.reply_to_author) : '',
likes: normalizeCount(comment.like_count ?? comment.vote_count),
created_at: normalizeUnixSeconds(comment.created_time),
url: context.questionId
? `https://www.zhihu.com/question/${context.questionId}/answer/${context.answerId}#comment-${descriptor.id}`
: typeof comment.url === 'string' ? comment.url : '',
content: stripHtml(comment.content || ''),
};
}
export function buildCommentRows(roots, repliesByRoot, context) {
const rows = [];
const globalIds = new Set();
for (let rootIndex = 0; rootIndex < roots.length; rootIndex += 1) {
const root = roots[rootIndex];
const rootDescriptor = describeComment(root, 'root');
if (globalIds.has(rootDescriptor.id)) {
throw new CommandExecutionError(`Zhihu answer comments reused id ${rootDescriptor.id} across graph roles`);
}
globalIds.add(rootDescriptor.id);
rows.push(toRow(root, rootDescriptor, {
rank: rows.length + 1, commentRank: rootIndex + 1, replyRank: 0, depth: 0,
}, context));
const childrenById = new Map();
for (const child of repliesByRoot.get(rootDescriptor.id) || []) {
const descriptor = describeComment(child, 'child', rootDescriptor.id);
if (globalIds.has(descriptor.id) || childrenById.has(descriptor.id)) {
throw new CommandExecutionError(`Zhihu answer comments reused id ${descriptor.id} across graph roles`);
}
globalIds.add(descriptor.id);
childrenById.set(descriptor.id, { comment: child, descriptor });
}
const depths = resolveDepths(rootDescriptor.id, childrenById);
let replyRank = 0;
for (const { comment, descriptor } of childrenById.values()) {View on GitHub (pinned to 49907e53dc)
Solutions
- Deduplicate roots by id before calling buildCommentRows: const seen=new Set(); roots=roots.filter(r=>!seen.has(r.id)&&seen.add(r.id))
- Re-fetch with stable ordering (--order latest) or a lower --limit to reduce page-shift overlap
- Check for API retries duplicating the root list in fetchRootComments
Example fix
// before const rows = buildCommentRows(roots, repliesByRoot, ctx); // after const seen = new Set(); const uniqueRoots = roots.filter(r => (seen.has(r.id) ? false : (seen.add(r.id), true))); const rows = buildCommentRows(uniqueRoots, repliesByRoot, ctx);
Defensive patterns
Strategy: validation
Validate before calling
const ids = roots.map(r => commentId(r));
if (new Set(ids).size !== ids.length) throw new Error('duplicate root ids before buildCommentRows'); Type guard
const isUniqueRoots = (roots) => new Set(roots.map(r => r.id)).size === roots.length;
Try / catch
try { rows = buildCommentRows(roots, repliesByRoot, ctx); }
catch (err) {
if (String(err.message).includes('reused id') && String(err.message).includes('graph roles')) {
roots = dedupeById(roots); rows = buildCommentRows(roots, repliesByRoot, ctx);
} else throw err;
} Prevention
- Always deduplicate root lists by id before building rows
- Use stable ordering to reduce pagination overlap duplicates
- Keep one source of truth for comment ids across roots/replies
When it happens
Trigger: fetchRootComments returned the same root comment id twice (overlapping pagination pages), or a comment already classified as a reply also appears in the root list.
Common situations: Zhihu API pagination shifting while new comments arrive (score-ordered pages overlap); API returning duplicated payloads after retry; version drift in the comments endpoint.
Related errors
- Zhihu answer comments reused id ${descriptor.id} across grap
- Zhihu ${label} returned malformed paging state
- Zhihu answer comments returned conflicting data for comment
- Zhihu ${label} pagination returned a repeated next URL
- Zhihu ${label} pagination exceeded its fetch budget
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/842633bc56a379e7.
Report an issue: GitHub.