{"record":{"id":"10b658fea118bd92","repo":"sipeed/picoclaw","slug":"feishu-file-send-api-error-code-d-msg-s","errorCode":null,"errorMessage":"feishu file send api error (code=%d msg=%s)","messagePattern":"feishu file send api error \\(code=(.+?) msg=(.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/feishu/feishu_64.go","lineNumber":1234,"sourceCode":"\n\t// Send file message\n\tcontent, _ := json.Marshal(map[string]string{\"file_key\": fileKey})\n\treq := larkim.NewCreateMessageReqBuilder().\n\t\tReceiveIdType(larkim.CreateMessageV1ReceiveIDTypeChatId).\n\t\tBody(larkim.NewCreateMessageReqBodyBuilder().\n\t\t\tReceiveId(chatID).\n\t\t\tMsgType(larkim.MsgTypeFile).\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 file send: %w\", err)\n\t}\n\tif !resp.Success() {\n\t\tc.invalidateTokenOnAuthError(resp.Code)\n\t\treturn fmt.Errorf(\"feishu file send api error (code=%d msg=%s)\", resp.Code, resp.Msg)\n\t}\n\treturn nil\n}\n\nfunc extractFeishuSenderID(sender *larkim.EventSender) string {\n\tif sender == nil || sender.SenderId == nil {\n\t\treturn \"\"\n\t}\n\n\tif sender.SenderId.UserId != nil && *sender.SenderId.UserId != \"\" {\n\t\treturn *sender.SenderId.UserId\n\t}\n\tif sender.SenderId.OpenId != nil && *sender.SenderId.OpenId != \"\" {\n\t\treturn *sender.SenderId.OpenId\n\t}\n\tif sender.SenderId.UnionId != nil && *sender.SenderId.UnionId != \"\" {\n\t\treturn *sender.SenderId.UnionId\n\t}","sourceCodeStart":1216,"sourceCodeEnd":1252,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/feishu/feishu_64.go#L1216-L1252","documentation":"Feishu rejected the file message send with a non-zero business code after the upload succeeded. invalidateTokenOnAuthError runs first (clears token on 99991663). Dominant causes: invalid/expired file_key, file_type mismatch between upload and send context, or missing permission on the target chat.","triggerScenarios":"Message.Create with msg_type=file fails: file_key expired between upload and send; key minted under a different app_id; audio uploaded as opus but sent to a chat/context where opus is disallowed; bot removed from chat; 99991663 stale token.","commonSituations":"Backoff-delayed retries outliving file_key validity; credential swap between upload and send; sending voice notes to chats where the bot lacks im:message permission.","solutions":["On invalid file_key, re-upload and send immediately as one unit.","Confirm upload and send use the same app credentials.","Verify feishuFileType matched the content at upload time (opus/mp4/stream) and the bot is in the target chat.","If code is 99991663, the token cache was already cleared — next attempt self-heals."],"exampleFix":"// before\nkey := uploadFile(ctx, f, name, fileType)\nerr := sendFileMsg(ctx, chatID, key)\n\n// after — pair upload+send so retries always use a fresh, valid key\nfor attempt := 0; attempt < 2; attempt++ {\n    key := uploadFile(ctx, f, name, fileType)\n    err = sendFileMsg(ctx, chatID, key)\n    if err == nil || !strings.Contains(err.Error(), \"code=230002\") {\n        break\n    }\n}","handlingStrategy":"validation","validationCode":"// validate the pairing before sending: file_key must come from an upload\n// made by this app, and feishuFileType must match the uploaded type\nif fileKey == \"\" {\n    return errors.New(\"cannot send file message without a fresh file_key\")\n}\nif uploadedType != feishuFileType {\n    return fmt.Errorf(\"file_type mismatch: uploaded %s, sending %s\", uploadedType, feishuFileType)\n}","typeGuard":"func isInvalidFileKey(err error) bool {\n    return strings.Contains(err.Error(), \"feishu file send api error\") && strings.Contains(err.Error(), \"code=230002\")\n}","tryCatchPattern":"if err := sendFileFlow(ctx, chatID, f, name, ft); err != nil {\n    if isInvalidFileKey(err) {\n        // expired/mismatched key: re-upload and resend as a unit\n        return sendFileFlow(ctx, chatID, f, name, ft)\n    }\n    return err\n}","preventionTips":["Never reuse file_keys across requests or apps","Match upload file_type to content (opus/mp4/stream)","Keep the bot in the target chat with im:message scope"],"tags":["feishu","send","go","lark-api","upload"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}