jackwener/OpenCLI · error · CommandExecutionError

Zhihu answer root comment ${id} had malformed child count

Error message

Zhihu answer root comment ${id} had malformed child count

What it means

fetchRepliesByRoot validates each root comment's child_comment_count before deciding whether to fetch its replies. A root whose count is missing or not a non-negative integer makes downstream loop/budget logic unreliable, so the library throws with the root's id.

Source

Thrown at clis/zhihu/answer-comments-helpers.js:184

}
async function fetchChildComments(page, rootId, limit) {
    const path = `/api/v4/comment_v5/comment/${rootId}/child_comment`;
    return fetchPages(page, {
        firstUrl: `https://www.zhihu.com${path}?limit=${PAGE_SIZE}&offset=0`,
        limit,
        label: 'answer child comments',
        role: 'child',
        expectedRootId: rootId,
        normalizeNext: (next) => normalizePageUrl(next, path, { limit: String(PAGE_SIZE), offset: null }),
    });
}
export async function fetchRepliesByRoot(page, roots, repliesLimit) {
    const repliesByRoot = new Map();
    if (repliesLimit === 0) return repliesByRoot;
    for (const root of roots) {
        const id = describeComment(root, 'root').id;
        if (!Number.isInteger(root.child_comment_count) || root.child_comment_count < 0) {
            throw new CommandExecutionError(`Zhihu answer root comment ${id} had malformed child count`);
        }
        if (root.child_comment_count === 0) continue;
        const children = await fetchChildComments(page, id, repliesLimit);
        if (children.length === 0) {
            throw new CommandExecutionError(`Zhihu answer root comment ${id} advertised replies but returned none`);
        }
        repliesByRoot.set(id, children);
    }
    return repliesByRoot;
}
function resolveDepths(rootId, childrenById) {
    const depths = new Map();
    for (const childId of childrenById.keys()) {
        if (depths.has(childId)) continue;
        const chain = [];
        const active = new Set();
        let cursor = childId;
        let depth = 0;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the root row: check whether child_comment_count exists and is a plain integer.
  2. Refresh the fixture/mock to match the current API response shape.
  3. Update the library if Zhihu renamed or retyped the count field.
  4. Refetch — if only one root is malformed it may be a transient partial response.

Example fix

// before: fixture from old API
{ id: 'r1', child_count: 3 }
// after
{ id: 'r1', child_comment_count: 3 }
Defensive patterns

Strategy: validation

Validate before calling

// validate root rows before processing
function rootsHaveValidCounts(roots) {
  return roots.every(r => Number.isInteger(r?.child_comment_count) && r.child_comment_count >= 0);
}

Type guard

function hasValidChildCount(root) {
  return Number.isInteger(root?.child_comment_count) && root.child_comment_count >= 0;
}

Try / catch

try {
  const byRoot = await fetchRepliesByRoot(page, roots, repliesLimit);
} catch (err) {
  const m = String(err.message).match(/root comment (\d+) had malformed child count/);
  if (m) {
    console.warn(`Skipping root ${m[1]} with malformed child_comment_count`);
    return fetchRepliesByRoot(page, roots.filter(r => String(r.id) !== m[1]), repliesLimit);
  }
  throw err;
}

Prevention

When it happens

Trigger: A root_comment row from /root_comment has child_comment_count undefined, null, a non-integer (float/string), or a negative number.

Common situations: Zhihu schema drift renaming the field (e.g. to child_count), API returning partial rows for very old comments, or mock fixtures built from an older API version lacking the field.

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/1c70d5865bcdd7cc. Report an issue: GitHub.