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

  1. Inspect the current placeholder format and update the matching logic (e.g. strip '回复 @' prefix and trailing ':')
  2. Handle nickname truncation: match on a prefix of the nickname or fuzzy-match if XHS elides it
  3. Re-locate the item and re-open the reply box if a re-render is suspected between match and verify
  4. 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

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.