xpzouying/xiaohongshu-mcp · error
滚动查找失败: %w
Error message
滚动查找失败: %w
What it means
During locate()'s scroll-and-search loop, each iteration calls page.Mouse.Scroll(0, 800, 5) to load more notifications. If rod's scroll call itself errors (page navigated away, tab closed, context canceled, browser disconnected), the error is wrapped with this prefix and returned.
Source
Thrown at xiaohongshu/notification_reply.go:127
return nil, 0, err
}
for i, r := range payload.MessageList {
if r.Comment.ID != commentID {
continue
}
if !r.visible() {
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)View on GitHub (pinned to 332d196854)
Solutions
- Check the wrapped %w cause: if it is context canceled/deadline, retry with a longer deadline or fix the caller's context
- Ensure no other code navigates or closes the same *rod.Page while Reply/Like is running
- Restart the browser instance and retry once; rod pages are not reusable after a disconnect
- Reduce reliance on very long scroll loops by fetching the notification list via the API-backed readTab data first
Example fix
// before
page := n.page.Timeout(3 * time.Minute)
// after
page, err := browser.Page(rod.NewContext())
if err != nil { return err }
page = page.Timeout(3 * time.Minute).SlowMotion(200 * time.Millisecond) // dedicated page, no sharing Defensive patterns
Strategy: retry
Try / catch
err := doReply(ctx, commentID, content)
if err != nil && strings.Contains(err.Error(), "滚动查找失败") {
// check the wrapped cause before retrying
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
return err // don't retry on caller cancellation
}
time.Sleep(2 * time.Second)
return doReply(freshCtx, commentID, content) // fresh page/context
} Prevention
- Never share one *rod.Page across goroutines
- Use generous but bounded page timeouts (3min is the library default)
- Monitor browser health; recreate the page on CDP disconnects
- Prefer API-level pagination over long scroll sessions when possible
When it happens
Trigger: Calling Reply/Like on a notification comment where, mid-scroll: the page object was closed or navigated elsewhere, the CDP connection dropped, the page was closed via rod, or the underlying browser process died.
Common situations: Long-running scroll loop exceeding the 3-minute page timeout; headless browser crash; another goroutine navigating the same *rod.Page concurrently; network hiccup killing the CDP websocket.
Related errors
AI-assisted analysis of xpzouying/xiaohongshu-mcp@332d196854 (2026-09-05).
Data as JSON: /api/errors/89ef77c3433fa070.
Report an issue: GitHub.