{"record":{"id":"9e7c437c2b39e39c","repo":"jackwener/OpenCLI","slug":"zhihu-answer-comments-returned-conflicting-data-fo","errorCode":null,"errorMessage":"Zhihu answer comments returned conflicting data for comment ${descriptor.id}","messagePattern":"Zhihu answer comments returned conflicting data for comment (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/zhihu/answer-comments-helpers.js","lineNumber":122,"sourceCode":"        role,\n        rootId,\n        parentId,\n        author: memberName(comment.author),\n        replyTo: role === 'child' ? memberName(comment.reply_to_author) : '',\n        content: stripHtml(comment.content || ''),\n    });\n    return { id, rootId, parentId, signature };\n}\nfunction addPageComments(byId, comments, role, expectedRootId) {\n    for (const comment of comments) {\n        const descriptor = describeComment(comment, role, expectedRootId);\n        const previous = byId.get(descriptor.id);\n        if (!previous) {\n            byId.set(descriptor.id, { comment, descriptor });\n            continue;\n        }\n        if (previous.descriptor.signature !== descriptor.signature) {\n            throw new CommandExecutionError(`Zhihu answer comments returned conflicting data for comment ${descriptor.id}`);\n        }\n        const oldCount = previous.comment.child_comment_count;\n        const newCount = comment.child_comment_count;\n        if (Number.isInteger(newCount) && (!Number.isInteger(oldCount) || newCount > oldCount)) {\n            previous.comment = { ...previous.comment, child_comment_count: newCount };\n        }\n    }\n}\nasync function fetchPages(page, options) {\n    const { firstUrl, limit, label, role, expectedRootId = '', normalizeNext, notFoundDetail = '' } = options;\n    const byId = new Map();\n    const visited = new Set();\n    const maxPages = Math.ceil(limit / PAGE_SIZE) + PAGE_OVERLAP_ALLOWANCE;\n    let pageCount = 0;\n    let url = firstUrl;\n    while (byId.size < limit) {\n        if (visited.has(url)) throw new CommandExecutionError(`Zhihu ${label} pagination returned a repeated next URL`);\n        if (pageCount >= maxPages) throw new CommandExecutionError(`Zhihu ${label} pagination exceeded its fetch budget`);","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/zhihu/answer-comments-helpers.js#L104-L140","documentation":"addPageComments deduplicates comments by id across pages. If the same comment id appears twice with a different signature (JSON of role, rootId, parentId, author, etc.), the API is serving contradictory data for one comment, so the library throws rather than silently choosing one version.","triggerScenarios":"Two pagination pages both contain comment X, but its fields (author name, content, parent links) differ between occurrences — detected via descriptor.signature inequality in addPageComments.","commonSituations":"Live comment edited between page fetches (author renamed, content changed), load-balanced API replicas serving different snapshots, or cache layers mixing old and new responses. Note: a merely larger child_comment_count is tolerated, so this fires only for other field changes.","solutions":["Refetch all pages quickly in one run so all copies of the comment come from the same snapshot.","Disable or bypass intermediate caches/proxies that can serve mixed-age pages.","If live edits are the cause, accept it as transient and retry; or downgrade to a page-size/order that fetches in a single page.","If you need edit-tolerance, patch addPageComments to compare only identity-critical signature fields."],"exampleFix":"// before: comparing full signature (author/content included)\nif (previous.descriptor.signature !== descriptor.signature) throw ...;\n// after: tolerate content-only edits\nconst stable = d => JSON.stringify({ role: d.role, rootId: d.rootId, parentId: d.parentId });\nif (stable(previous.descriptor) !== stable(descriptor)) throw ...;","handlingStrategy":"retry","validationCode":"// detect cross-page duplicates with differing payloads before they reach the library\nfunction scanForConflicts(pages) {\n  const seen = new Map();\n  for (const page of pages) for (const c of page.data) {\n    const key = String(c.id);\n    const snap = JSON.stringify({ a: c.author, p: c.reply_comment_id });\n    if (seen.has(key) && seen.get(key) !== snap) return key;\n    seen.set(key, snap);\n  }\n  return null;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const comments = await fetchRootComments(page, answerId, order, limit);\n} catch (err) {\n  if (String(err.message).includes('returned conflicting data')) {\n    await sleep(2000); // let replicas converge / snapshot stabilize\n    return fetchRootComments(page, answerId, order, limit);\n  }\n  throw err;\n}","preventionTips":["Fetch all pages in one fast run to minimize mid-fetch edits.","Prefer a single-page fetch when the comment count fits in PAGE_SIZE.","Bypass caches for comment endpoints to avoid mixed-age snapshots."],"tags":["api","pagination","data-integrity","zhihu"],"backgroundTag":"inconsistent-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}