{"record":{"id":"89ef77c3433fa070","repo":"xpzouying/xiaohongshu-mcp","slug":"w-89ef77","errorCode":null,"errorMessage":"滚动查找失败: %w","messagePattern":"滚动查找失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"xiaohongshu/notification_reply.go","lineNumber":127,"sourceCode":"\t\t\treturn nil, 0, err\n\t\t}\n\n\t\tfor i, r := range payload.MessageList {\n\t\t\tif r.Comment.ID != commentID {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !r.visible() {\n\t\t\t\treturn nil, 0, fmt.Errorf(\"该评论已删除或不可见，不能回复: %s\", commentID)\n\t\t\t}\n\t\t\treturn &r, i, nil\n\t\t}\n\n\t\tif !payload.HasMore {\n\t\t\treturn nil, 0, fmt.Errorf(\"未找到评论 %s，它可能不在「评论和@」里或已被清理\", commentID)\n\t\t}\n\n\t\tif err := page.Mouse.Scroll(0, 800, 5); err != nil {\n\t\t\treturn nil, 0, fmt.Errorf(\"滚动查找失败: %w\", err)\n\t\t}\n\t\thumanize.Delay(ctx, humanize.BetweenScroll)\n\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn nil, 0, err\n\t\t}\n\t}\n\treturn nil, 0, fmt.Errorf(\"翻找 %d 轮仍未定位到评论 %s\", maxRounds, commentID)\n}\n\n// verifyReplyTarget 用输入框的 placeholder 核对回复对象。\nfunc verifyReplyTarget(input *rod.Element, nickname string) error {\n\tplaceholder, err := input.Attribute(\"placeholder\")\n\tif err != nil || placeholder == nil {\n\t\treturn fmt.Errorf(\"读不到回复框提示文字，无法确认回复对象，已中止\")\n\t}\n\tif nickname != \"\" && !strings.Contains(*placeholder, nickname) {\n\t\treturn fmt.Errorf(\"回复对象不符：期望 %q，实际提示为 %q，已中止\", nickname, *placeholder)","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/xiaohongshu/notification_reply.go#L109-L145","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\npage := n.page.Timeout(3 * time.Minute)\n// after\npage, err := browser.Page(rod.NewContext())\nif err != nil { return err }\npage = page.Timeout(3 * time.Minute).SlowMotion(200 * time.Millisecond) // dedicated page, no sharing","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := doReply(ctx, commentID, content)\nif err != nil && strings.Contains(err.Error(), \"滚动查找失败\") {\n    // check the wrapped cause before retrying\n    if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {\n        return err // don't retry on caller cancellation\n    }\n    time.Sleep(2 * time.Second)\n    return doReply(freshCtx, commentID, content) // fresh page/context\n}","preventionTips":["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"],"tags":["browser-automation","go","rod","scroll"],"backgroundTag":"rod-scroll-failed","analyzedSha":"332d196854a9eac0d2b8c2c0e3d0cc43139d724c","analyzedAt":"2026-09-05T22:22:55.988Z","contentChangedAt":"2026-09-05T22:22:55.988Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}