chenhg5/cc-connect · error
dingtalk: send file failed: status=%d, body=%s
Error message
dingtalk: send file failed: status=%d, body=%s
What it means
DingTalk's oToMessages batchSend endpoint returned a non-200 HTTP status when sending a file message. The library logs the response body at debug level and surfaces the status code plus raw body in the error. This is an application-level rejection by the DingTalk API (auth, media, or payload problem), not a transport failure.
Source
Thrown at platform/dingtalk/dingtalk.go:1164
"https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend",
bytes.NewReader(body))
if err != nil {
return fmt.Errorf("dingtalk: create file request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-acs-dingtalk-access-token", token)
resp, err := p.httpClient.Do(req)
if err != nil {
return fmt.Errorf("dingtalk: send file request: %w", err)
}
defer func() { _ = resp.Body.Close() }()
respBody, _ := io.ReadAll(resp.Body)
slog.Debug("dingtalk: oToMessages file response", "status", resp.StatusCode, "body", string(respBody))
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("dingtalk: send file failed: status=%d, body=%s", resp.StatusCode, string(respBody))
}
slog.Info("dingtalk: file message sent", "media_id", mediaID, "name", name, "user", rc.senderStaffId)
return nil
}
var _ core.FileSender = (*Platform)(nil)
// SendAudio uploads audio bytes to DingTalk and sends a voice message.
// Implements core.AudioSender interface.
// Uses DingTalk oToMessages API with msgKey: "sampleAudio" (voice messages).
// DingTalk voice messages only support ogg/amr formats (not mp3).
func (p *Platform) SendAudio(ctx context.Context, rctx any, audio []byte, format string) error {
rc, ok := rctx.(replyContext)
if !ok {
return fmt.Errorf("dingtalk: SendAudio: invalid reply context type %T", rctx)
}
View on GitHub (pinned to 4000b2338a)
Solutions
- Read the status and body in the error: 401/invalidAuthentication → refresh getAccessToken; 400 with mediaId error → re-upload the media
- Confirm the target user has an active session with the bot (oToMessages requires it)
- Check DingTalk rate limits and add throttling/retry on 429
- Enable debug logging to capture the full response body
Example fix
// before
token, err := p.getAccessToken()
if err != nil { return err }
// send with cached possibly-expired token
// after
token, err := p.getAccessToken() // ensure token is refreshed, cache with expiry
if err != nil { return err }
if strings.Contains(errBody, "invalidAuthentication") { p.invalidateToken(); retry once } Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check: ensure the user has interacted with the bot and media was freshly uploaded
if time.Since(mediaUploadedAt) > 24*time.Hour { /* re-upload media before sending */ } Try / catch
if err := p.SendFile(ctx, rc, data, name); err != nil {
var se *dingtalk.StatusError
if errors.As(err, &se) && se.StatusCode == 401 { refreshToken(); retry() }
if se != nil && se.StatusCode == 429 { backoff(); retry() }
} Prevention
- Refresh the access token proactively before expiry
- Re-upload media if the mediaId is older than DingTalk's expiry window
- Ensure target users have an active bot session (oToMessages requirement)
- Log response bodies at info level in production to diagnose API rejections
- Add 429-aware rate limiting on outbound robot messages
When it happens
Trigger: Calling SendFile when the access token is expired/invalid (401), the mediaId is invalid or expired (media upload expired), the staffId/userId doesn't exist, the bot isn't in the target conversation, or a rate limit is hit (429).
Common situations: Access token expired between fetch and send, file uploaded with wrong media type, sending to a user who never interacted with the bot (DingTalk requires prior interaction for robot oToMessages), or DingTalk-side throttling.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- get access token: %w
- create AI card: status=%d, body=%s
- AI card delivery failed: %s
- stream AI card: status=%d, body=%s
- get download URL: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/43be1ccb0233273c.
Report an issue: GitHub.