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

  1. Check the wrapped %w cause: if it is context canceled/deadline, retry with a longer deadline or fix the caller's context
  2. Ensure no other code navigates or closes the same *rod.Page while Reply/Like is running
  3. Restart the browser instance and retry once; rod pages are not reusable after a disconnect
  4. 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

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.