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

  1. Re-find each option element immediately before its click (no caching across iterations)
  2. Move the mouse back into the panel before each subsequent click to keep it open
  3. Retry the whole filter application once: re-open panel and apply remaining filters
  4. 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

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.