{"record":{"id":"fa3a31dab782e1eb","repo":"sipeed/picoclaw","slug":"feishu-send-text-w","errorCode":null,"errorMessage":"feishu send text: %w","messagePattern":"feishu send text: %w","errorType":"exception","errorClass":"ErrTemporary","httpStatus":null,"severity":"error","filePath":"pkg/channels/feishu/feishu_64.go","lineNumber":1120,"sourceCode":"\treturn \"\", nil\n}\n\n// sendText sends a plain text message to a chat (fallback when card fails).\nfunc (c *FeishuChannel) sendText(ctx context.Context, chatID, text string) (string, error) {\n\tcontent, _ := json.Marshal(map[string]string{\"text\": text})\n\n\treq := larkim.NewCreateMessageReqBuilder().\n\t\tReceiveIdType(larkim.CreateMessageV1ReceiveIDTypeChatId).\n\t\tBody(larkim.NewCreateMessageReqBodyBuilder().\n\t\t\tReceiveId(chatID).\n\t\t\tMsgType(larkim.MsgTypeText).\n\t\t\tContent(string(content)).\n\t\t\tBuild()).\n\t\tBuild()\n\n\tresp, err := c.client.Im.V1.Message.Create(ctx, req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"feishu send text: %w\", channels.ErrTemporary)\n\t}\n\n\tif !resp.Success() {\n\t\treturn \"\", fmt.Errorf(\"feishu text api error (code=%d msg=%s): %w\", resp.Code, resp.Msg, channels.ErrTemporary)\n\t}\n\n\tlogger.DebugCF(\"feishu\", \"Feishu text message sent (fallback)\", map[string]any{\n\t\t\"chat_id\": chatID,\n\t})\n\n\tif resp.Data != nil && resp.Data.MessageId != nil {\n\t\treturn *resp.Data.MessageId, nil\n\t}\n\treturn \"\", nil\n}\n\n// sendImage uploads an image and sends it as a message.\nfunc (c *FeishuChannel) sendImage(ctx context.Context, chatID string, file *os.File) error {","sourceCodeStart":1102,"sourceCodeEnd":1138,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/feishu/feishu_64.go#L1102-L1138","documentation":"The Lark SDK Message.Create call returned a Go error before an API response body existed — a transport-level failure (connection refused, DNS error, TLS failure, client timeout). The channel deliberately discards the underlying err and wraps only channels.ErrTemporary, so the send is retried with exponential backoff but the true cause (network vs marshalling) is lost from the message.","triggerScenarios":"msg_type=text Create call fails at transport: no egress to open.feishu.cn, HTTP_PROXY/HTTPS_PROXY pointing at a dead proxy, client timeout on a slow link, or SDK request-marshalling failure on the text content.","commonSituations":"Container/k8s pod without network egress; corporate TLS-intercepting proxy; transient ISP blip; proxy env vars set for a different environment.","solutions":["Verify egress from the same host: curl https://open.feishu.cn/open-apis/ (or a TCP dial to open.feishu.cn:443).","Rely on the built-in retry (ErrTemporary) and check logs for whether a later attempt succeeded.","If persistent, note this path drops the SDK error — temporarily log err before the wrap (or patch it, see exampleFix) to see the real cause.","Check HTTP_PROXY/HTTPS_PROXY/NO_PROXY values in the runtime environment."],"exampleFix":"// before (feishu_64.go:1120) — underlying err is discarded\nreturn \"\", fmt.Errorf(\"feishu send text: %w\", channels.ErrTemporary)\n\n// after (Go 1.20+ multi-%w): keep the cause AND the retry sentinel\nreturn \"\", fmt.Errorf(\"feishu send text: %w: %w\", err, channels.ErrTemporary)","handlingStrategy":"retry","validationCode":"conn, derr := net.DialTimeout(\"tcp\", \"open.feishu.cn:443\", 2*time.Second)\nif derr != nil {\n    // defer the send: egress is down, it would only fail as temporary\n}\nconn.Close()","typeGuard":"func isTemporarySendErr(err error) bool {\n    return errors.Is(err, channels.ErrTemporary)\n}","tryCatchPattern":"if _, err := ch.Send(ctx, msg); err != nil {\n    if errors.Is(err, channels.ErrTemporary) {\n        // backoff and retry; transport causes are dropped from this message, check network health separately\n    }\n}","preventionTips":["Health-check egress to open.feishu.cn from the runtime host","Keep HTTP_PROXY/HTTPS_PROXY correct for the deployment","Alert on consecutive temporary failures — this error hides the underlying cause","Patch the wrap to include the SDK error for diagnosability"],"tags":["feishu","network","go","transport","retry"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}