{"record":{"id":"d59ceba91370a848","repo":"sipeed/picoclaw","slug":"feishu-react-w","errorCode":null,"errorMessage":"feishu react: %w","messagePattern":"feishu react: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/channels/feishu/feishu_64.go","lineNumber":452,"sourceCode":"\tif len(candidates) > 0 {\n\t\tchosenEmoji = candidates[rand.Intn(len(candidates))]\n\t}\n\n\treq := larkim.NewCreateMessageReactionReqBuilder().\n\t\tMessageId(messageID).\n\t\tBody(larkim.NewCreateMessageReactionReqBodyBuilder().\n\t\t\tReactionType(larkim.NewEmojiBuilder().EmojiType(chosenEmoji).Build()).\n\t\t\tBuild()).\n\t\tBuild()\n\n\tresp, err := c.client.Im.V1.MessageReaction.Create(ctx, req)\n\tif err != nil {\n\t\tlogger.ErrorCF(\"feishu\", \"Failed to add reaction\", map[string]any{\n\t\t\t\"emoji\":      chosenEmoji,\n\t\t\t\"message_id\": messageID,\n\t\t\t\"error\":      err.Error(),\n\t\t})\n\t\treturn func() {}, fmt.Errorf(\"feishu react: %w\", err)\n\t}\n\tif !resp.Success() {\n\t\tc.invalidateTokenOnAuthError(resp.Code)\n\t\tlogger.ErrorCF(\"feishu\", \"Reaction API error\", map[string]any{\n\t\t\t\"emoji\":      chosenEmoji,\n\t\t\t\"message_id\": messageID,\n\t\t\t\"code\":       resp.Code,\n\t\t\t\"msg\":        resp.Msg,\n\t\t})\n\t\treturn func() {}, fmt.Errorf(\"feishu react api error (code=%d msg=%s)\", resp.Code, resp.Msg)\n\t}\n\n\tvar reactionID string\n\tif resp.Data != nil && resp.Data.ReactionId != nil {\n\t\treactionID = *resp.Data.ReactionId\n\t}\n\tif reactionID == \"\" {\n\t\treturn func() {}, nil","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/feishu/feishu_64.go#L434-L470","documentation":"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.","triggerScenarios":"POST reaction API fails at the network layer: timeout, connection reset, DNS failure while attaching an emoji reaction to a message.","commonSituations":"Transient network blips right after a successful send; proxies rate-limiting a burst of send+react calls; slow mobile uplinks.","solutions":["Retry once or twice with a short delay - transport errors are transient","If it persists, verify connectivity to open.feishu.cn","Downgrade the failure to a warning: the message itself was sent successfully"],"exampleFix":"// before\nundo, err := ch.React(ctx, chatID, msgID, emoji)\nif err != nil {\n\treturn err // emoji failure kills a successful send\n}\n\n// after\nundo, err := ch.React(ctx, chatID, msgID, emoji)\nif err != nil {\n\tlogger.Warn(\"reaction failed (non-fatal)\", \"err\", err)\n\tundo = func() {}\n}","handlingStrategy":"retry","validationCode":"null","typeGuard":"func isReactTransportError(err error) bool {\n\treturn err != nil && strings.HasPrefix(err.Error(), \"feishu react: \") && !strings.Contains(err.Error(), \"feishu react api error\")\n}","tryCatchPattern":"undo, err := ch.React(ctx, chatID, msgID, emoji)\nfor i := 0; isReactTransportError(err) && i < 2; i++ {\n\ttime.Sleep(300 * time.Millisecond)\n\tundo, err = ch.React(ctx, chatID, msgID, emoji)\n}\nif err != nil {\n\tlogger.Warn(\"reaction failed (cosmetic)\", \"err\", err)\n\tundo = func() {}\n}","preventionTips":["Retry reactions briefly but never propagate their failure to the send flow","The returned undo func is a no-op on failure - safe to call unconditionally","Batch reactions away from the critical send moment to dodge bursts","Watch logged emoji/message_id pairs to spot systematic offenders"],"tags":["feishu","reaction","network","non-fatal","lark-sdk"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}