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
- Replace fixed sleeps with a retry/poll: loop findFooterByText with a deadline (e.g. up to 10s)
- Log all footer texts when not found to detect markup/text changes
- Verify the originality dialog actually opened before searching the footer
- 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
- Replace fixed sleeps with polling waits
- Capture a page screenshot on failure to see whether the dialog opened
- Assert dialog presence before searching the footer
- Track frontend DOM changes; keep keywords/selectors in one config spot
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.