{"record":{"id":"973d2d3f553a3477","repo":"xpzouying/xiaohongshu-mcp","slug":"w-973d2d","errorCode":null,"errorMessage":"悬停筛选按钮失败: %w","messagePattern":"悬停筛选按钮失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"xiaohongshu/search.go","lineNumber":117,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// 注意 .Context(ctx) 会替换掉 NewSearchAction 里设的 60s deadline，必须在其后重新 Timeout，\n\t// 否则搜索页不 stable 时 MustWaitStable/MustWait 会永久挂起（无 deadline 可依赖）。\n\tpage := s.page.Context(ctx).Timeout(60 * time.Second)\n\n\tsearchURL := makeSearchURL(keyword)\n\tpage.MustNavigate(searchURL)\n\tpage.MustWaitStable()\n\tpage.MustWait(`() => window.__INITIAL_STATE__ !== undefined`)\n\thumanize.Delay(ctx, humanize.AfterNavigate)\n\n\tif len(pending) > 0 {\n\t\t// 悬停在筛选按钮上展开面板\n\t\tfilterButton := page.MustElement(`div.filter`)\n\t\tif err := humanize.Hover(filterButton); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"悬停筛选按钮失败: %w\", err)\n\t\t}\n\t\thumanize.Delay(ctx, humanize.BeforeClick)\n\n\t\t// 等待筛选面板出现\n\t\tpage.MustWait(`() => document.querySelector('div.filter-panel') !== null`)\n\n\t\t// 记下筛选前的结果，用来判断筛选后的数据什么时候到位\n\t\tbefore := readFeedIDs(page)\n\n\t\t// 用 ClickNoWait：筛选面板是 hover 浮层，rod 的 WaitInteractable 会误判被遮挡而死等；\n\t\t// ClickNoWait 移进面板内选项（维持 hover、面板不关）再点。\n\t\tfor _, pf := range pending {\n\t\t\toption, err := findFilterOption(page, pf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\thumanize.Delay(ctx, humanize.BeforeClick)\n\t\t\tif err := humanize.ClickNoWait(option); err != nil {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/xiaohongshu/search.go#L99-L135","documentation":"Search() applies filters by hovering the div.filter button to open the panel. humanize.Hover wraps rod's mouse-move with human-like behavior; any underlying rod error (element detached, page navigated, context timeout, overlay intercept) is wrapped with this prefix.","triggerScenarios":"Search called with non-empty filters when: the search results page layout changed so div.filter is missing/covered, the page did not finish rendering before hover, the 60s page timeout fired, or a modal/overlay intercepts the pointer.","commonSituations":"XHS A/B test removing or renaming the filter button; slow network leaving the page unstable; running headless with a tiny viewport where the button is off-screen or covered; login wall appearing instead of results.","solutions":["Inspect the live search page: confirm div.filter still exists; update the selector if XHS changed markup","Add an explicit wait for the filter button before hovering instead of relying on MustWaitStable","Increase page timeout for slow networks","Check you are actually logged in — a login wall replaces the results page and breaks all selectors"],"exampleFix":"// before\nfilterButton := page.MustElement(`div.filter`)\nif err := humanize.Hover(filterButton); err != nil { ... }\n// after\nfilterButton, err := page.Timeout(10*time.Second).Element(`div.filter`)\nif err != nil { return nil, fmt.Errorf(\"筛选按钮未出现，页面结构可能已变化: %w\", err) }\nif err := humanize.Hover(filterButton); err != nil { ... }","handlingStrategy":"try-catch","validationCode":"// pre-flight: make sure the results page actually rendered the filter button\nif _, err := page.Timeout(10*time.Second).Element(`div.filter`); err != nil {\n    return fmt.Errorf(\"search page missing filter button — likely login wall or UI change: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"res, err := action.Search(ctx, keyword, filters...)\nif err != nil && strings.Contains(err.Error(), \"悬停筛选按钮失败\") {\n    // once: re-navigate and retry with fresh page state\n    log.Printf(\"filter hover failed; retrying once with fresh page\")\n    return action.Search(ctx, keyword, filters...)\n}","preventionTips":["Use a normal desktop viewport in headless mode","Verify login state before filtered searches","After XHS UI deploys, smoke-test the filtered search path","Keep timeouts generous on slow networks"],"tags":["browser-automation","go","rod","hover"],"backgroundTag":"hover-failed","analyzedSha":"332d196854a9eac0d2b8c2c0e3d0cc43139d724c","analyzedAt":"2026-09-05T22:22:55.988Z","contentChangedAt":"2026-09-05T22:22:55.988Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}