sipeed/picoclaw · warning

feishu react: %w

Error message

feishu react: %w

What it means

FeishuChannel's React path called lark SDK Im.V1.MessageReaction.Create and the SDK returned a transport-level error before parsing a business response. The function returns a no-op undo func plus this wrapped error, and logs the emoji, message_id, and cause. Reactions are auxiliary UX; failing to add one should not fail the send pipeline.

Source

Thrown at pkg/channels/feishu/feishu_64.go:452

	if len(candidates) > 0 {
		chosenEmoji = candidates[rand.Intn(len(candidates))]
	}

	req := larkim.NewCreateMessageReactionReqBuilder().
		MessageId(messageID).
		Body(larkim.NewCreateMessageReactionReqBodyBuilder().
			ReactionType(larkim.NewEmojiBuilder().EmojiType(chosenEmoji).Build()).
			Build()).
		Build()

	resp, err := c.client.Im.V1.MessageReaction.Create(ctx, req)
	if err != nil {
		logger.ErrorCF("feishu", "Failed to add reaction", map[string]any{
			"emoji":      chosenEmoji,
			"message_id": messageID,
			"error":      err.Error(),
		})
		return func() {}, fmt.Errorf("feishu react: %w", err)
	}
	if !resp.Success() {
		c.invalidateTokenOnAuthError(resp.Code)
		logger.ErrorCF("feishu", "Reaction API error", map[string]any{
			"emoji":      chosenEmoji,
			"message_id": messageID,
			"code":       resp.Code,
			"msg":        resp.Msg,
		})
		return func() {}, fmt.Errorf("feishu react api error (code=%d msg=%s)", resp.Code, resp.Msg)
	}

	var reactionID string
	if resp.Data != nil && resp.Data.ReactionId != nil {
		reactionID = *resp.Data.ReactionId
	}
	if reactionID == "" {
		return func() {}, nil

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Retry once or twice with a short delay - transport errors are transient
  2. If it persists, verify connectivity to open.feishu.cn
  3. Downgrade the failure to a warning: the message itself was sent successfully

Example fix

// before
undo, err := ch.React(ctx, chatID, msgID, emoji)
if err != nil {
	return err // emoji failure kills a successful send
}

// after
undo, err := ch.React(ctx, chatID, msgID, emoji)
if err != nil {
	logger.Warn("reaction failed (non-fatal)", "err", err)
	undo = func() {}
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

func isReactTransportError(err error) bool {
	return err != nil && strings.HasPrefix(err.Error(), "feishu react: ") && !strings.Contains(err.Error(), "feishu react api error")
}

Try / catch

undo, err := ch.React(ctx, chatID, msgID, emoji)
for i := 0; isReactTransportError(err) && i < 2; i++ {
	time.Sleep(300 * time.Millisecond)
	undo, err = ch.React(ctx, chatID, msgID, emoji)
}
if err != nil {
	logger.Warn("reaction failed (cosmetic)", "err", err)
	undo = func() {}
}

Prevention

When it happens

Trigger: POST reaction API fails at the network layer: timeout, connection reset, DNS failure while attaching an emoji reaction to a message.

Common situations: Transient network blips right after a successful send; proxies rate-limiting a burst of send+react calls; slow mobile uplinks.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/d59ceba91370a848. Report an issue: GitHub.