{"record":{"id":"ff5081f224783f61","repo":"xpzouying/xiaohongshu-mcp","slug":"q-footer","errorCode":null,"errorMessage":"未找到包含%q的弹窗 footer","messagePattern":"未找到包含%q的弹窗 footer","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"xiaohongshu/publish.go","lineNumber":1090,"sourceCode":"\ttime.Sleep(300 * time.Millisecond)\n\treturn nil\n}\n\nfunc findFooterByText(page *rod.Page, keyword string) (*rod.Element, error) {\n\tfooters, err := page.Elements(\"div.footer\")\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"查找弹窗 footer 失败\")\n\t}\n\tfor _, footer := range footers {\n\t\ttext, err := footer.Text()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(text, keyword) {\n\t\t\treturn footer, nil\n\t\t}\n\t}\n\treturn nil, errors.Errorf(\"未找到包含%q的弹窗 footer\", keyword)\n}\n\n// checkFooterCheckbox 勾选 footer 内的自定义 checkbox（未勾选时才点）。\nfunc checkFooterCheckbox(footer *rod.Element) error {\n\tcb, err := footer.Element(\"div.d-checkbox\")\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"未找到须知 checkbox\")\n\t}\n\n\t// 只读判断当前是否已勾选（隐藏 input.checked 或 simulator 上的 checked 态）\n\tchecked, err := cb.Eval(`() => {\n\t\tconst input = this.querySelector('input[type=\"checkbox\"]');\n\t\treturn (input && input.checked) || this.querySelector('.checked') !== null;\n\t}`)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"读取 checkbox 状态失败\")\n\t}\n\tif checked.Value.Bool() {","sourceCodeStart":1072,"sourceCodeEnd":1108,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/xiaohongshu/publish.go#L1072-L1108","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ntime.Sleep(800 * time.Millisecond)\nfooter, err := findFooterByText(page, \"声明原创\")\n// after\nvar footer *rod.Element\nvar err error\nfor i := 0; i < 20; i++ {\n    footer, err = findFooterByText(page, \"声明原创\")\n    if err == nil { break }\n    time.Sleep(500 * time.Millisecond)\n}","handlingStrategy":"validation","validationCode":"// poll instead of one-shot after a fixed sleep\nfunc waitFooter(page *rod.Page, keyword string, timeout time.Duration) (*rod.Element, error) {\n    deadline := time.Now().Add(timeout)\n    for time.Now().Before(deadline) {\n        f, err := findFooterByText(page, keyword)\n        if err == nil { return f, nil }\n        time.Sleep(500 * time.Millisecond)\n    }\n    return nil, fmt.Errorf(\"footer %q not found within %s\", keyword, timeout)\n}","typeGuard":null,"tryCatchPattern":"if err := confirmOriginalDeclaration(page); err != nil {\n    if strings.Contains(err.Error(), \"未找到包含\") {\n        // dialog never rendered: check eligibility/screenshot for diagnosis\n    }\n}","preventionTips":["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"],"tags":["go-rod","dom-selector","ui-change"],"backgroundTag":"selector-not-found","analyzedSha":"332d196854a9eac0d2b8c2c0e3d0cc43139d724c","analyzedAt":"2026-09-05T22:22:55.988Z","contentChangedAt":"2026-09-05T22:22:55.988Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}