xpzouying/xiaohongshu-mcp · error

翻找 %d 轮仍未定位到评论 %s

Error message

翻找 %d 轮仍未定位到评论 %s

What it means

locate() caps the scroll-and-search loop at maxRounds=20. If the commentID is still not found after 20 iterations — and HasMore stayed true each time so the earlier 'not found / cleaned' branch never fired — it gives up with this error. It means the comment exists deeper than 20 scroll-screens or pagination never terminates.

Source

Thrown at xiaohongshu/notification_reply.go:135

				return nil, 0, fmt.Errorf("该评论已删除或不可见,不能回复: %s", commentID)
			}
			return &r, i, nil
		}

		if !payload.HasMore {
			return nil, 0, fmt.Errorf("未找到评论 %s,它可能不在「评论和@」里或已被清理", commentID)
		}

		if err := page.Mouse.Scroll(0, 800, 5); err != nil {
			return nil, 0, fmt.Errorf("滚动查找失败: %w", err)
		}
		humanize.Delay(ctx, humanize.BetweenScroll)

		if err := ctx.Err(); err != nil {
			return nil, 0, err
		}
	}
	return nil, 0, fmt.Errorf("翻找 %d 轮仍未定位到评论 %s", maxRounds, commentID)
}

// verifyReplyTarget 用输入框的 placeholder 核对回复对象。
func verifyReplyTarget(input *rod.Element, nickname string) error {
	placeholder, err := input.Attribute("placeholder")
	if err != nil || placeholder == nil {
		return fmt.Errorf("读不到回复框提示文字,无法确认回复对象,已中止")
	}
	if nickname != "" && !strings.Contains(*placeholder, nickname) {
		return fmt.Errorf("回复对象不符:期望 %q,实际提示为 %q,已中止", nickname, *placeholder)
	}
	return nil
}

// waitReplyAccepted 等待回复提交完成。
func waitReplyAccepted(item *rod.Element, timeout time.Duration) error {
	deadline := time.Now().Add(timeout)
	for time.Now().Before(deadline) {

View on GitHub (pinned to 332d196854)

Solutions

  1. Increase maxRounds if you must reach deep history (accepting longer runtime and more detection risk)
  2. Use the XHS web/API notification pagination directly to page to the comment instead of scrolling
  3. Fall back to locating the comment on its note page and replying there
  4. For old comments, prefer replying promptly — treat >1 week old comments as high-risk for this error

Example fix

// before
const maxRounds = 20
// after
// page via readTab pagination (offset cursor) instead of mouse scroll:
for payload.HasMore {
    payload = n.readTabPage(page, TabMentions, cursor); cursor++
    if r, i := match(payload, commentID); r != nil { return r, i, nil }
}
Defensive patterns

Strategy: fallback

Validate before calling

// skip clearly-stale comments before invoking the deep scroll
if time.Since(commentCreatedAt) > 7*24*time.Hour {
    return fmt.Errorf("skip: comment %s too old for notification-reply flow", commentID)
}

Try / catch

_, err := action.Reply(ctx, commentID, content)
if err != nil && strings.Contains(err.Error(), "翻找") {
    log.Printf("deep-scroll miss for %s; falling back to note-page reply", commentID)
    return replyViaNotePage(ctx, feedID, commentID, content)
}

Prevention

When it happens

Trigger: Reply/Like with a valid but very old commentID buried hundreds of notifications deep; a platform quirk where payload.HasMore never becomes false, so the loop runs all 20 rounds without a definitive answer.

Common situations: Accounts with very active notification feeds (the target scrolled out quickly); replying to comments older than a few weeks on busy accounts; virtualized list rendering where old items get recycled before they are matched.

Related errors


AI-assisted analysis of xpzouying/xiaohongshu-mcp@332d196854 (2026-09-05). Data as JSON: /api/errors/8adccfcf58f2cc94. Report an issue: GitHub.