jackwener/OpenCLI · error · CommandExecutionError

Zhihu answer comment ${id} did not identify its immediate pa

Error message

Zhihu answer comment ${id} did not identify its immediate parent

What it means

A child comment must declare its immediate parent via reply_comment_id. describeComment throws when the normalized parentId is falsy for a role='child' row, because the library builds reply depth chains (resolveDepths) by walking parentId links; a child without a parent breaks tree reconstruction.

Source

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

    }
    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 };
}
function addPageComments(byId, comments, role, expectedRootId) {
    for (const comment of comments) {
        const descriptor = describeComment(comment, role, expectedRootId);
        const previous = byId.get(descriptor.id);
        if (!previous) {
            byId.set(descriptor.id, { comment, descriptor });
            continue;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check the raw row: does reply_comment_id exist and normalize? If absent, treat the API payload as inconsistent and refetch.
  2. If the parent comment was deleted, skip that subtree rather than expecting the library to attach it.
  3. Fix fixtures/mocks to include reply_comment_id on every child row.
  4. Update the library if Zhihu renamed the parent-reference field.

Example fix

// before
{ id: 'c2', reply_root_comment_id: 'r1' }
// after
{ id: 'c2', reply_root_comment_id: 'r1', reply_comment_id: 'r1' }
Defensive patterns

Strategy: type-guard

Validate before calling

// drop child rows lacking a parent link before tree building
function validChildRows(rows) {
  return rows.filter(r => r.reply_comment_id != null && String(r.reply_comment_id) !== '');
}

Type guard

function declaresParent(row) {
  return row != null && typeof row === 'object' &&
    row.reply_comment_id !== undefined && row.reply_comment_id !== null &&
    String(row.reply_comment_id).trim() !== '';
}

Try / catch

try {
  const tree = await buildCommentTree(answerId);
} catch (err) {
  if (String(err.message).includes('did not identify its immediate parent')) {
    console.warn('A reply lost its parent (likely deleted); flattening it under the root');
    return buildCommentTree(answerId, { orphanPolicy: 'attach-to-root' });
  }
  throw err;
}

Prevention

When it happens

Trigger: A row fetched as a reply has reply_comment_id missing, null, or normalizing to empty, while reply_root_comment_id is present (so it passed the root provenance check).

Common situations: Zhihu returning a reply row whose direct parent comment was deleted (reply_comment_id dangling to a removed id) or schema drift where the field is now named differently. Also hit with hand-made fixtures omitting reply_comment_id.

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/a89c3875a91ccee8. Report an issue: GitHub.