xpzouying/xiaohongshu-mcp · error
点击筛选选项「%s」失败: %w
Error message
点击筛选选项「%s」失败: %w
What it means
After opening the filter panel, Search() locates each pending filter option via findFilterOption and clicks it with humanize.ClickNoWait (chosen because the panel is a hover overlay where rod's WaitInteractable misjudges occlusion). If the click fails (element detached when the hover overlay closed, overlay dismissed, timeout), the error is wrapped with this prefix naming the option text.
Source
Thrown at xiaohongshu/search.go:136
}
humanize.Delay(ctx, humanize.BeforeClick)
// 等待筛选面板出现
page.MustWait(`() => document.querySelector('div.filter-panel') !== null`)
// 记下筛选前的结果,用来判断筛选后的数据什么时候到位
before := readFeedIDs(page)
// 用 ClickNoWait:筛选面板是 hover 浮层,rod 的 WaitInteractable 会误判被遮挡而死等;
// ClickNoWait 移进面板内选项(维持 hover、面板不关)再点。
for _, pf := range pending {
option, err := findFilterOption(page, pf)
if err != nil {
return nil, err
}
humanize.Delay(ctx, humanize.BeforeClick)
if err := humanize.ClickNoWait(option); err != nil {
return nil, fmt.Errorf("点击筛选选项「%s」失败: %w", pf.option, err)
}
}
waitFeedsChanged(page, before, 15*time.Second)
}
result := page.MustEval(`() => {
if (window.__INITIAL_STATE__ &&
window.__INITIAL_STATE__.search &&
window.__INITIAL_STATE__.search.feeds) {
const feeds = window.__INITIAL_STATE__.search.feeds;
const feedsData = feeds.value !== undefined ? feeds.value : feeds._value;
if (feedsData) {
return JSON.stringify(feedsData);
}
}
return "";
}`).String()View on GitHub (pinned to 332d196854)
Solutions
- Re-find each option element immediately before its click (no caching across iterations)
- Move the mouse back into the panel before each subsequent click to keep it open
- Retry the whole filter application once: re-open panel and apply remaining filters
- If a specific option consistently fails, check whether XHS renders it as multiple div.tags and target the visible one
Example fix
// before
option, err := findFilterOption(page, pf)
humanize.Delay(ctx, humanize.BeforeClick)
if err := humanize.ClickNoWait(option); err != nil { ... }
// after
// re-find right before each click + one retry
var clickErr error
for retry := 0; retry < 2; retry++ {
option, err := findFilterOption(page, pf)
if err != nil { clickErr = err; continue }
if clickErr = humanize.ClickNoWait(option); clickErr == nil { break }
}
if clickErr != nil { return nil, fmt.Errorf("点击筛选选项「%s」失败: %w", pf.option, clickErr) } Defensive patterns
Strategy: retry
Validate before calling
// verify each option is present and visible before the click sequence starts
for _, pf := range pendingFilters {
if _, err := findFilterOption(page, pf); err != nil {
return fmt.Errorf("option %q not found before clicking", pf.option)
}
} Try / catch
res, err := action.Search(ctx, keyword, filters...)
if err != nil && strings.Contains(err.Error(), "点击筛选选项") {
// re-open the panel and re-apply from scratch; stale elements won't heal in place
time.Sleep(1 * time.Second)
return action.Search(ctx, keyword, filters...)
} Prevention
- Always re-find option elements right before each click; never cache across clicks
- Keep the pointer inside the panel between clicks so the hover overlay stays open
- Apply filters one at a time and wait for the feed list to change between them
- Test with a viewport large enough to render the whole panel
When it happens
Trigger: Search with filters when: the hover panel closes between finding and clicking an option (mouse drifted, another overlay), the option element got re-rendered (stale node), ClickNoWait's pointer events were intercepted, or findFilterOption matched a hidden duplicate tag div that is not clickable.
Common situations: Applying multiple filters where clicking one option re-renders the panel, invalidating the next option element; viewport too small so the panel scrolls/closes; headless mouse behavior differences.
Related errors
AI-assisted analysis of xpzouying/xiaohongshu-mcp@332d196854 (2026-09-05).
Data as JSON: /api/errors/cf0a15d985716d67.
Report an issue: GitHub.