{"record":{"id":"a88cf1ced1442dc5","repo":"xpzouying/xiaohongshu-mcp","slug":"error-a88cf1","errorCode":null,"errorMessage":"切换到上传视频失败","messagePattern":"切换到上传视频失败","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"xiaohongshu/publish_video.go","lineNumber":46,"sourceCode":"\tpp := page.Timeout(300 * time.Second)\n\n\tif err := pp.Navigate(urlOfPublic); err != nil {\n\t\treturn nil, errors.Wrap(err, \"导航到发布页面失败\")\n\t}\n\n\t// 使用 WaitLoad 代替 WaitIdle（更宽松）\n\tif err := pp.WaitLoad(); err != nil {\n\t\tlogrus.Warnf(\"等待页面加载出现问题: %v，继续尝试\", err)\n\t}\n\ttime.Sleep(2 * time.Second)\n\n\tif err := pp.WaitDOMStable(time.Second, 0.1); err != nil {\n\t\tlogrus.Warnf(\"等待 DOM 稳定出现问题: %v，继续尝试\", err)\n\t}\n\ttime.Sleep(1 * time.Second)\n\n\tif err := mustClickPublishTab(pp, \"上传视频\"); err != nil {\n\t\treturn nil, errors.Wrap(err, \"切换到上传视频失败\")\n\t}\n\n\ttime.Sleep(1 * time.Second)\n\n\treturn &PublishAction{page: pp}, nil\n}\n\n// PublishVideo 上传视频并提交\nfunc (p *PublishAction) PublishVideo(ctx context.Context, content PublishVideoContent) error {\n\tif content.VideoPath == \"\" {\n\t\treturn errors.New(\"视频不能为空\")\n\t}\n\n\t// 重设超时：.Context(ctx) 会替换掉 NewPublishVideoAction 里 Timeout(300s) 的 deadline\n\tpage := p.page.Context(ctx).Timeout(300 * time.Second)\n\n\tif err := uploadVideo(page, content.VideoPath); err != nil {\n\t\treturn errors.Wrap(err, \"小红书上传视频失败\")","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/xiaohongshu/publish_video.go#L28-L64","documentation":"NewPublishVideoAction 已导航到发布页，但 mustClickPublishTab 切换到「上传视频」TAB 失败（TAB 未找到或被浮层遮挡），无法进入视频上传模式。具体原因见被包装的底层错误。","triggerScenarios":"mustClickPublishTab 返回错误：发布页 DOM 改版导致 tab 选择器失效；页面未登录被重定向到登录页（没有 tab 可点）；页面加载慢，固定 sleep 后 tab 未渲染；「上传视频」文案被平台更名。","commonSituations":"cookie 过期后进入页面实际是登录页；小红书改版把 tab 文字改成「上传视频」以外的形式或换了组件库；弱网下 WaitDOMStable 失败（仅 warn 不中断）后 tab 还没出来；默认停在「上传图文」tab 且查找范围不对。","solutions":["截图或保存页面 HTML 确认当前页面是发布页还是登录页，登录态失效则重新登录","检查 mustClickPublishTab 内的 tab 选择器与文案是否与当前页面一致，按需更新","用 rod 的 WaitRequestIdle / 轮询等待 tab 元素出现，替代固定 time.Sleep","为该步骤增加一次重试（刷新页面后重新查找 tab）"],"exampleFix":"// before\ntime.Sleep(1 * time.Second)\nif err := mustClickPublishTab(pp, \"上传视频\"); err != nil {\n    return nil, errors.Wrap(err, \"切换到上传视频失败\")\n}\n// after\nvar tabErr error\nfor i := 0; i < 3; i++ {\n    if tabErr = mustClickPublishTab(pp, \"上传视频\"); tabErr == nil {\n        break\n    }\n    _ = pp.Reload()\n    _ = pp.WaitLoad()\n    time.Sleep(2 * time.Second)\n}\nif tabErr != nil {\n    return nil, errors.Wrap(tabErr, \"切换到上传视频失败\")\n}","handlingStrategy":"validation","validationCode":"// 调用前确认当前确实是发布页而非登录页\nurl := pp.MustInfo().URL\nif strings.Contains(url, \"login\") || !strings.Contains(url, \"creator\") {\n    return errors.New(\"登录态失效，被重定向到: \" + url)\n}","typeGuard":"func onPublishPage(pp *rod.Page) bool {\n    el, err := pp.Element(\"text=上传视频\")\n    return err == nil && el != nil\n}","tryCatchPattern":"if err := mustClickPublishTab(pp, \"上传视频\"); err != nil {\n    snapshot, _ := pp.Screenshot(true, nil)\n    _ = os.WriteFile(\"tab-fail.png\", snapshot, 0o644)\n    return errors.Wrap(err, \"切换到上传视频失败，已保存页面截图\")\n}","preventionTips":["每次发布前校验登录态/URL，避免在登录页上找 tab","tab 查找失败时保存截图便于排查改版","用元素等待替代固定 sleep","把 tab 文案/选择器收敛为常量，改版时只改一处"],"tags":["tab","selector","ui-automation","go-rod"],"backgroundTag":"ui-tab-switch-failed","analyzedSha":"332d196854a9eac0d2b8c2c0e3d0cc43139d724c","analyzedAt":"2026-09-05T22:22:55.988Z","contentChangedAt":"2026-09-05T22:22:55.988Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}