xpzouying/xiaohongshu-mcp · error

未找到包含%q的弹窗 footer

Error message

未找到包含%q的弹窗 footer

What it means

findFooterByText scanned all div.footer elements and none contained the expected keyword (e.g. '原创声明须知' or '声明原创'). This means the confirmation dialog did not render, rendered without a footer, or the footer markup/text changed. Callers partially tolerate this for the '须知' footer (warn only) but hard-fail for the '声明原创' footer.

Source

Thrown at xiaohongshu/publish.go:1090

	time.Sleep(300 * time.Millisecond)
	return nil
}

func findFooterByText(page *rod.Page, keyword string) (*rod.Element, error) {
	footers, err := page.Elements("div.footer")
	if err != nil {
		return nil, errors.Wrap(err, "查找弹窗 footer 失败")
	}
	for _, footer := range footers {
		text, err := footer.Text()
		if err != nil {
			continue
		}
		if strings.Contains(text, keyword) {
			return footer, nil
		}
	}
	return nil, errors.Errorf("未找到包含%q的弹窗 footer", keyword)
}

// checkFooterCheckbox 勾选 footer 内的自定义 checkbox(未勾选时才点)。
func checkFooterCheckbox(footer *rod.Element) error {
	cb, err := footer.Element("div.d-checkbox")
	if err != nil {
		return errors.Wrap(err, "未找到须知 checkbox")
	}

	// 只读判断当前是否已勾选(隐藏 input.checked 或 simulator 上的 checked 态)
	checked, err := cb.Eval(`() => {
		const input = this.querySelector('input[type="checkbox"]');
		return (input && input.checked) || this.querySelector('.checked') !== null;
	}`)
	if err != nil {
		return errors.Wrap(err, "读取 checkbox 状态失败")
	}
	if checked.Value.Bool() {

View on GitHub (pinned to 332d196854)

Solutions

  1. Replace fixed sleeps with a retry/poll: loop findFooterByText with a deadline (e.g. up to 10s)
  2. Log all footer texts when not found to detect markup/text changes
  3. Verify the originality dialog actually opened before searching the footer
  4. Update the keyword or selector constants if the site DOM changed

Example fix

// before
time.Sleep(800 * time.Millisecond)
footer, err := findFooterByText(page, "声明原创")
// after
var footer *rod.Element
var err error
for i := 0; i < 20; i++ {
    footer, err = findFooterByText(page, "声明原创")
    if err == nil { break }
    time.Sleep(500 * time.Millisecond)
}
Defensive patterns

Strategy: validation

Validate before calling

// poll instead of one-shot after a fixed sleep
func waitFooter(page *rod.Page, keyword string, timeout time.Duration) (*rod.Element, error) {
    deadline := time.Now().Add(timeout)
    for time.Now().Before(deadline) {
        f, err := findFooterByText(page, keyword)
        if err == nil { return f, nil }
        time.Sleep(500 * time.Millisecond)
    }
    return nil, fmt.Errorf("footer %q not found within %s", keyword, timeout)
}

Try / catch

if err := confirmOriginalDeclaration(page); err != nil {
    if strings.Contains(err.Error(), "未找到包含") {
        // dialog never rendered: check eligibility/screenshot for diagnosis
    }
}

Prevention

When it happens

Trigger: confirmOriginalDeclaration runs before the dialog finishes rendering (fixed 800ms sleep insufficient), the originality dialog never appears (account not eligible, feature unavailable), or Xiaohongshu changed footer class/text so strings.Contains never matches.

Common situations: Slow network making 800ms wait insufficient; front-end redesign renaming .footer or button text; new-user accounts where the original-declaration entry doesn't open a dialog.

Related errors


AI-assisted analysis of xpzouying/xiaohongshu-mcp@332d196854 (2026-09-05). Data as JSON: /api/errors/ff5081f224783f61. Report an issue: GitHub.