sipeed/picoclaw · error
feishu file upload api error (code=%d msg=%s)
Error message
feishu file upload api error (code=%d msg=%s)
What it means
Feishu's file upload endpoint returned a non-zero business code (uploadResp.Success() false). invalidateTokenOnAuthError runs first, clearing the token cache on 99991663. Typical codes: 230002 invalid file (size >30MB, unsupported/mismatched file_type), 99991663 token, 99991400 rate limit.
Source
Thrown at pkg/channels/feishu/feishu_64.go:1209
feishuFileType = "mp4"
}
// Upload file to get file_key
uploadReq := larkim.NewCreateFileReqBuilder().
Body(larkim.NewCreateFileReqBodyBuilder().
FileType(feishuFileType).
FileName(filename).
File(file).
Build()).
Build()
uploadResp, err := c.client.Im.V1.File.Create(ctx, uploadReq)
if err != nil {
return fmt.Errorf("feishu file upload: %w", err)
}
if !uploadResp.Success() {
c.invalidateTokenOnAuthError(uploadResp.Code)
return fmt.Errorf("feishu file upload api error (code=%d msg=%s)", uploadResp.Code, uploadResp.Msg)
}
if uploadResp.Data == nil || uploadResp.Data.FileKey == nil {
return fmt.Errorf("feishu file upload: no file_key returned")
}
fileKey := *uploadResp.Data.FileKey
// Send file message
content, _ := json.Marshal(map[string]string{"file_key": fileKey})
req := larkim.NewCreateMessageReqBuilder().
ReceiveIdType(larkim.CreateMessageV1ReceiveIDTypeChatId).
Body(larkim.NewCreateMessageReqBodyBuilder().
ReceiveId(chatID).
MsgType(larkim.MsgTypeFile).
Content(string(content)).
Build()).
Build()
View on GitHub (pinned to 49183d7e8d)
Solutions
- Match the code: 230002 -> check 30MB limit and that file_type matches the actual content (audio=opus, video=mp4, else stream); 99991663 -> fix credentials; 99991400 -> throttle.
- Pre-validate size and extension-to-type mapping before upload (see exampleFix).
- Grant im:file permission to the app.
- Re-encode oversized media instead of forwarding raw.
Example fix
// before
err := ch.SendMedia(ctx, msg) // 230002 after full upload of an oversized file
// after — validate before uploading
if fi, serr := f.Stat(); serr == nil && fi.Size() > 30<<20 {
return fmt.Errorf("file %s exceeds Feishu 30MB limit (%d bytes)", name, fi.Size())
}
wantType := map[string]string{"audio": "opus", "video": "mp4"}[fileType]
if wantType == "" { wantType = "stream" } // must match the value used in CreateFileReqBodyBuilder Defensive patterns
Strategy: validation
Validate before calling
const feishuMaxFile = 30 << 20
if fi, serr := f.Stat(); serr != nil || fi.Size() > feishuMaxFile {
return fmt.Errorf("file exceeds Feishu 30MB limit or is unreadable")
}
want := map[string]string{"audio": "opus", "video": "mp4"}[fileType]
if want == "" { want = "stream" }
// ensure the same 'want' is what the channel passes as feishuFileType Type guard
func isFeishuFileUploadAPIErr(err error) bool {
return strings.Contains(err.Error(), "feishu file upload api error (code=")
} Try / catch
if err := ch.SendMedia(ctx, msg); err != nil {
if strings.Contains(err.Error(), "code=230002") {
// invalid file/type: fix size, encoding, or type mapping — retrying same bytes fails again
}
if strings.Contains(err.Error(), "code=99991663") {
// token issue; cache already invalidated
}
} Prevention
- Map part types to opus/mp4/stream exactly like the channel does
- Re-encode oversized media under 30MB
- Grant im:file scope
- Throttle uploads to stay under QPS limits
When it happens
Trigger: Im.V1.File.Create with FileType('stream'|'opus'|'mp4') fails: file exceeds 30MB; audio/video sent with the wrong feishuFileType mapping; 99991663 stale token; app lacks im:file scope; burst uploads rate-limited.
Common situations: Forwarding large media from other channels; sending a .ogg recorded as opus but declared stream; credentials rotated while old token cached.
Related errors
- feishu image upload api error (code=%d msg=%s)
- feishu image send api error (code=%d msg=%s)
- feishu file send api error (code=%d msg=%s)
- feishu api error (code=%d msg=%s): %w
- feishu text api error (code=%d msg=%s): %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/3a16f804a80c4a98.
Report an issue: GitHub.