{"record":{"id":"1c95796c002402fd","repo":"xpzouying/xiaohongshu-mcp","slug":"error-1c9579","errorCode":null,"errorMessage":"输入搜索关键词失败","messagePattern":"输入搜索关键词失败","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"xiaohongshu/publish.go","lineNumber":1274,"sourceCode":"// searchAndSelectProduct 搜索并选择商品\nfunc searchAndSelectProduct(ctx context.Context, page *rod.Page, modal *rod.Element, keyword string) error {\n\tslog.Info(\"搜索商品\", \"keyword\", keyword)\n\n\t// 1. 获取搜索框\n\tsearchInput, err := modal.Element(`input[placeholder=\"搜索商品ID 或 商品名称\"]`)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"未找到商品搜索框\")\n\t}\n\n\t// 2. 清空并输入关键词。SelectAllText 走 Eval(this.select())，只改选区、不额外\n\t// 派发事件，暂时保留（换键盘全选要区分 Ctrl/Cmd）。\n\tif err := searchInput.SelectAllText(); err != nil {\n\t\tslog.Warn(\"选择搜索框文本失败\", \"error\", err)\n\t}\n\ttime.Sleep(100 * time.Millisecond)\n\n\tif err := humanize.Type(ctx, searchInput, keyword); err != nil {\n\t\treturn errors.Wrap(err, \"输入搜索关键词失败\")\n\t}\n\ttime.Sleep(300 * time.Millisecond)\n\n\t// 3. 触发搜索（模拟键盘 Enter）\n\tif err := page.Keyboard.Press(input.Enter); err != nil {\n\t\treturn errors.Wrap(err, \"触发搜索失败\")\n\t}\n\n\t// 4. 等待搜索结果加载\n\ttime.Sleep(1 * time.Second)\n\n\t// 等待 loading 消失（使用与工作代码相同的选择器）\n\tdeadline := time.Now().Add(10 * time.Second)\n\tfor time.Now().Before(deadline) {\n\t\tloading, err := modal.Element(\".goods-list-loading\")\n\t\tif err != nil || loading == nil {\n\t\t\tbreak\n\t\t}","sourceCodeStart":1256,"sourceCodeEnd":1292,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/xiaohongshu/publish.go#L1256-L1292","documentation":"searchAndSelectProduct 定位到搜索框后（先尝试全选清空，失败仅告警），humanize.Type 输入商品关键词失败：输入框失焦或输入事件被拦截。","triggerScenarios":"输入过程中搜索框被重新渲染导致 detached；元素无法 focus（被禁用/遮挡）；ctx 已取消或超时；CDP keyboard 事件注入失败；搜索框被前端重置。","commonSituations":"搜索框在聚焦后触发重渲染（React 受控组件重建）；ctx 上游 deadline 已到；页面因登录态失效发生跳转；前端改版后 input 为只读或被自定义组件接管。","solutions":["展开 wrapped error 查看根因：ctx 超时则放宽上游 deadline；detached 则重新查找输入框后重试","输入前确认 searchInput 可交互（Visible + 非只读），必要时先点击聚焦","失败后重试：重取 input 句柄再执行 humanize.Type，而不是复用旧句柄","确认 keyword 非空且不含导致特殊处理的字符；必要时改用 searchInput.Input(keyword) 简化路径"],"exampleFix":"// before\nif err := humanize.Type(ctx, searchInput, keyword); err != nil {\n    return errors.Wrap(err, \"输入搜索关键词失败\")\n}\n// after\nif err := humanize.Type(ctx, searchInput, keyword); err != nil {\n    var retryErr error\n    searchInput, retryErr = modal.Element(`input[placeholder*=\"搜索商品\"]`)\n    if retryErr != nil {\n        return errors.Wrap(err, \"输入搜索关键词失败\")\n    }\n    if err := humanize.Type(ctx, searchInput, keyword); err != nil {\n        return errors.Wrap(err, \"输入搜索关键词失败\")\n    }\n}","handlingStrategy":"retry","validationCode":"ro, _ := searchInput.Attribute(\"readonly\")\nvis, _ := searchInput.Visible()\nif !vis || ro != nil {\n    return errors.New(\"搜索框不可输入\")\n}\nif err := ctx.Err(); err != nil {\n    return err // ctx 已取消，不必尝试输入\n}","typeGuard":null,"tryCatchPattern":"if err := humanize.Type(ctx, searchInput, keyword); err != nil {\n    if ctx.Err() != nil {\n        return fmt.Errorf(\"输入搜索关键词失败(上下文取消): %w\", err)\n    }\n    // 重新定位输入框重试一次\n}","preventionTips":["输入前检查 ctx 未取消并保留足够 deadline","输入后校验输入框 value 是否为 keyword","失败时重取元素句柄再重试","保持页面稳定后再输入，避免输入中途重渲染","keyword 先做 trim/合法性校验"],"tags":["automation","rod","keyboard","input"],"backgroundTag":"keyboard-input-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"}