{"record":{"id":"2b8c21c3a1364b5a","repo":"xpzouying/xiaohongshu-mcp","slug":"q-q","errorCode":null,"errorMessage":"回复对象不符：期望 %q，实际提示为 %q，已中止","messagePattern":"回复对象不符：期望 %q，实际提示为 %q，已中止","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"xiaohongshu/notification_reply.go","lineNumber":145,"sourceCode":"\t\t\treturn nil, 0, fmt.Errorf(\"滚动查找失败: %w\", err)\n\t\t}\n\t\thumanize.Delay(ctx, humanize.BetweenScroll)\n\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn nil, 0, err\n\t\t}\n\t}\n\treturn nil, 0, fmt.Errorf(\"翻找 %d 轮仍未定位到评论 %s\", maxRounds, commentID)\n}\n\n// verifyReplyTarget 用输入框的 placeholder 核对回复对象。\nfunc verifyReplyTarget(input *rod.Element, nickname string) error {\n\tplaceholder, err := input.Attribute(\"placeholder\")\n\tif err != nil || placeholder == nil {\n\t\treturn fmt.Errorf(\"读不到回复框提示文字，无法确认回复对象，已中止\")\n\t}\n\tif nickname != \"\" && !strings.Contains(*placeholder, nickname) {\n\t\treturn fmt.Errorf(\"回复对象不符：期望 %q，实际提示为 %q，已中止\", nickname, *placeholder)\n\t}\n\treturn nil\n}\n\n// waitReplyAccepted 等待回复提交完成。\nfunc waitReplyAccepted(item *rod.Element, timeout time.Duration) error {\n\tdeadline := time.Now().Add(timeout)\n\tfor time.Now().Before(deadline) {\n\t\thas, _, err := item.Has(`textarea.comment-input`)\n\t\tif err == nil && !has {\n\t\t\treturn nil\n\t\t}\n\t\ttime.Sleep(300 * time.Millisecond)\n\t}\n\treturn fmt.Errorf(\"回复未确认成功：发送后输入框仍未收起（可能被限制或发送失败）\")\n}\n","sourceCodeStart":127,"sourceCodeEnd":162,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/xiaohongshu/notification_reply.go#L127-L162","documentation":"verifyReplyTarget() compares the reply box's placeholder text against the nickname of the comment author being replied to. If the placeholder does not contain the nickname, the library aborts the reply to prevent replying to the wrong comment — a mis-click or wrong item index would silently post to the wrong thread.","triggerScenarios":"Reply() located an item whose rendered reply input belongs to a different comment than the one matched (stale index after list re-render, duplicated nicknames confusing ordering, or the placeholder format changed so nickname substring no longer matches).","commonSituations":"XHS changed placeholder format (e.g. now '回复 @昵称：' vs plain nickname, or truncates long nicknames with '...' so Contains fails); notifications list re-rendered between locate() and clicking 回复, shifting the textarea to another comment's input; nickname containing characters that get HTML-escaped in the placeholder.","solutions":["Inspect the current placeholder format and update the matching logic (e.g. strip '回复 @' prefix and trailing '：')","Handle nickname truncation: match on a prefix of the nickname or fuzzy-match if XHS elides it","Re-locate the item and re-open the reply box if a re-render is suspected between match and verify","Verify the target.Comment.ID of the item whose input you grabbed equals the requested commentID before typing"],"exampleFix":"// before\nif nickname != \"\" && !strings.Contains(*placeholder, nickname) {\n    return fmt.Errorf(\"回复对象不符...\")\n}\n// after\nclean := strings.TrimPrefix(*placeholder, \"回复 @\")\nclean = strings.TrimSuffix(clean, \"：\")\nif nickname != \"\" && !strings.Contains(clean, truncateForCompare(nickname, 12)) {\n    return fmt.Errorf(\"回复对象不符...\")\n}","handlingStrategy":"try-catch","validationCode":"// confirm the item you located still holds the target comment right before Reply\nif item.Comment.ID != commentID { return fmt.Errorf(\"stale locate for %s\", commentID) }\n// and confirm the nickname format survives placeholder rendering (no elision assumptions)","typeGuard":null,"tryCatchPattern":"_, err := action.Reply(ctx, commentID, content)\nif err != nil && strings.Contains(err.Error(), \"回复对象不符\") {\n    // abort is intentional — do NOT retry immediately; re-locate first\n    log.Printf(\"target mismatch for %s: re-locating\", commentID)\n    return retryWithRelocate(ctx, commentID, content)\n}","preventionTips":["Re-fetch items between locate() and the click if the page may re-render","Match placeholder with normalization (strip '回复 @', trailing '：', handle truncation)","Abort-on-mismatch is a feature: never loosen it just to make batch runs complete"],"tags":["browser-automation","go","safety-check","ui-change"],"backgroundTag":"reply-target-mismatch","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"}