{"record":{"id":"4e4143d9060988a0","repo":"xpzouying/xiaohongshu-mcp","slug":"class","errorCode":null,"errorMessage":"读取旧版发布按钮 class 属性失败","messagePattern":"读取旧版发布按钮 class 属性失败","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"xiaohongshu/publish.go","lineNumber":534,"sourceCode":"\tfor _, oldButton := range oldButtons {\n\t\tif !isElementVisible(oldButton) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif disabled, err := oldButton.Attribute(\"disabled\"); err != nil {\n\t\t\treturn nil, \"\", errors.Wrap(err, \"读取旧版发布按钮 disabled 属性失败\")\n\t\t} else if disabled != nil {\n\t\t\treturn &publishButton{elem: oldButton}, \"旧版发布按钮 disabled\", nil\n\t\t}\n\n\t\tif ariaDisabled, err := oldButton.Attribute(\"aria-disabled\"); err != nil {\n\t\t\treturn nil, \"\", errors.Wrap(err, \"读取旧版发布按钮 aria-disabled 属性失败\")\n\t\t} else if ariaDisabled != nil && *ariaDisabled == \"true\" {\n\t\t\treturn &publishButton{elem: oldButton}, \"旧版发布按钮 aria-disabled=true\", nil\n\t\t}\n\n\t\tif cls, err := oldButton.Attribute(\"class\"); err != nil {\n\t\t\treturn nil, \"\", errors.Wrap(err, \"读取旧版发布按钮 class 属性失败\")\n\t\t} else if cls != nil && hasExactClass(*cls, \"disabled\") {\n\t\t\treturn &publishButton{elem: oldButton}, \"旧版发布按钮包含 disabled class\", nil\n\t\t}\n\n\t\treturn &publishButton{elem: oldButton}, \"\", nil\n\t}\n\n\treturn nil, \"\", nil\n}\n\nfunc clickPublishWidget(page *rod.Page, widget *rod.Element) error {\n\tif err := widget.ScrollIntoView(); err != nil {\n\t\treturn errors.Wrap(err, \"滚动新版发布按钮到可视区域失败\")\n\t}\n\ttime.Sleep(200 * time.Millisecond)\n\n\tshape, err := widget.Shape()\n\tif err != nil {","sourceCodeStart":516,"sourceCodeEnd":552,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/xiaohongshu/publish.go#L516-L552","documentation":"findPublishButton wraps the error from Element.Attribute(\"class\") on a visible legacy publish button when the CDP read fails. As with the other attribute reads, this is a transport/element-lifecycle failure, not a 'button has disabled class' result. The library needs the class list to detect the disabled-styled variant, so it aborts the search.","triggerScenarios":"Element detached during the attribute-read sequence (after `disabled` and `aria-disabled` reads succeeded); page navigation/closed tab; CDP protocol error; browser OOM crash.","commonSituations":"Fast re-render of the publish button right as the check runs; flaky connection to a remote Chrome instance; script continuing to use a rod.Page after its window was closed.","solutions":["Retry the flow — findPublishButton is invoked inside a polling loop and will pick up fresh elements","Wait for the DOM to settle (page.WaitDOMStable / WaitStable) before starting the publish-button wait","Detect a dead browser/tab and re-launch or re-open the page instead of retrying against the stale handle","Keep rod updated to pick up CDP race-condition fixes"],"exampleFix":"// before\nfor {\n    btn, reason, err := findPublishButton(page)\n// after\nfor {\n    _ = page.WaitStable(300 * time.Millisecond)\n    btn, reason, err := findPublishButton(page)","handlingStrategy":"retry","validationCode":"if err := page.WaitStable(300 * time.Millisecond); err != nil { return err }\noldButtons, err := page.Elements(\".publish-page-publish-btn button.bg-red\")\nif err != nil || len(oldButtons) == 0 { return errors.New(\"旧版发布按钮不可用\") }","typeGuard":"func elementAttached(page *rod.Page, sel string) bool {\n    els, err := page.Elements(sel)\n    return err == nil && len(els) > 0\n}","tryCatchPattern":"btn, reason, err := waitForPublishButtonClickable(page)\nif err != nil && strings.Contains(err.Error(), \"class 属性失败\") {\n    time.Sleep(500 * time.Millisecond)\n    btn, reason, err = waitForPublishButtonClickable(page)\n}","preventionTips":["Retry transient CDP attribute errors a fixed number of times before giving up","Stabilize the page before classification so class mutations settle","Detect closed targets (browser each/monitor) and re-open proactively","Log the raw underlying error to distinguish detach vs connection failure"],"tags":["browser-automation","rod","cdp","element-detached"],"backgroundTag":"cdp-element-attribute-failed","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"}