xpzouying/xiaohongshu-mcp · warning
回复对象不符:期望 %q,实际提示为 %q,已中止
Error message
回复对象不符:期望 %q,实际提示为 %q,已中止
What it means
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.
Source
Thrown at xiaohongshu/notification_reply.go:145
return nil, 0, fmt.Errorf("滚动查找失败: %w", err)
}
humanize.Delay(ctx, humanize.BetweenScroll)
if err := ctx.Err(); err != nil {
return nil, 0, err
}
}
return nil, 0, fmt.Errorf("翻找 %d 轮仍未定位到评论 %s", maxRounds, commentID)
}
// verifyReplyTarget 用输入框的 placeholder 核对回复对象。
func verifyReplyTarget(input *rod.Element, nickname string) error {
placeholder, err := input.Attribute("placeholder")
if err != nil || placeholder == nil {
return fmt.Errorf("读不到回复框提示文字,无法确认回复对象,已中止")
}
if nickname != "" && !strings.Contains(*placeholder, nickname) {
return fmt.Errorf("回复对象不符:期望 %q,实际提示为 %q,已中止", nickname, *placeholder)
}
return nil
}
// waitReplyAccepted 等待回复提交完成。
func waitReplyAccepted(item *rod.Element, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
has, _, err := item.Has(`textarea.comment-input`)
if err == nil && !has {
return nil
}
time.Sleep(300 * time.Millisecond)
}
return fmt.Errorf("回复未确认成功:发送后输入框仍未收起(可能被限制或发送失败)")
}
View on GitHub (pinned to 332d196854)
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
Example fix
// before
if nickname != "" && !strings.Contains(*placeholder, nickname) {
return fmt.Errorf("回复对象不符...")
}
// after
clean := strings.TrimPrefix(*placeholder, "回复 @")
clean = strings.TrimSuffix(clean, ":")
if nickname != "" && !strings.Contains(clean, truncateForCompare(nickname, 12)) {
return fmt.Errorf("回复对象不符...")
} Defensive patterns
Strategy: try-catch
Validate before calling
// confirm the item you located still holds the target comment right before Reply
if item.Comment.ID != commentID { return fmt.Errorf("stale locate for %s", commentID) }
// and confirm the nickname format survives placeholder rendering (no elision assumptions) Try / catch
_, err := action.Reply(ctx, commentID, content)
if err != nil && strings.Contains(err.Error(), "回复对象不符") {
// abort is intentional — do NOT retry immediately; re-locate first
log.Printf("target mismatch for %s: re-locating", commentID)
return retryWithRelocate(ctx, commentID, content)
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
AI-assisted analysis of xpzouying/xiaohongshu-mcp@332d196854 (2026-09-05).
Data as JSON: /api/errors/2b8c21c3a1364b5a.
Report an issue: GitHub.