{"record":{"id":"43be1ccb0233273c","repo":"chenhg5/cc-connect","slug":"dingtalk-send-file-failed-status-d-body-s","errorCode":null,"errorMessage":"dingtalk: send file failed: status=%d, body=%s","messagePattern":"dingtalk: send file failed: status=(.+?), body=(.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":1164,"sourceCode":"\t\t\"https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend\",\n\t\tbytes.NewReader(body))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dingtalk: create file request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"x-acs-dingtalk-access-token\", token)\n\n\tresp, err := p.httpClient.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dingtalk: send file request: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tslog.Debug(\"dingtalk: oToMessages file response\", \"status\", resp.StatusCode, \"body\", string(respBody))\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"dingtalk: send file failed: status=%d, body=%s\", resp.StatusCode, string(respBody))\n\t}\n\n\tslog.Info(\"dingtalk: file message sent\", \"media_id\", mediaID, \"name\", name, \"user\", rc.senderStaffId)\n\treturn nil\n}\n\nvar _ core.FileSender = (*Platform)(nil)\n\n// SendAudio uploads audio bytes to DingTalk and sends a voice message.\n// Implements core.AudioSender interface.\n// Uses DingTalk oToMessages API with msgKey: \"sampleAudio\" (voice messages).\n// DingTalk voice messages only support ogg/amr formats (not mp3).\nfunc (p *Platform) SendAudio(ctx context.Context, rctx any, audio []byte, format string) error {\n\trc, ok := rctx.(replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"dingtalk: SendAudio: invalid reply context type %T\", rctx)\n\t}\n","sourceCodeStart":1146,"sourceCodeEnd":1182,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L1146-L1182","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\ntoken, err := p.getAccessToken()\nif err != nil { return err }\n// send with cached possibly-expired token\n// after\ntoken, err := p.getAccessToken() // ensure token is refreshed, cache with expiry\nif err != nil { return err }\nif strings.Contains(errBody, \"invalidAuthentication\") { p.invalidateToken(); retry once }","handlingStrategy":"try-catch","validationCode":"// pre-check: ensure the user has interacted with the bot and media was freshly uploaded\nif time.Since(mediaUploadedAt) > 24*time.Hour { /* re-upload media before sending */ }","typeGuard":null,"tryCatchPattern":"if err := p.SendFile(ctx, rc, data, name); err != nil {\n    var se *dingtalk.StatusError\n    if errors.As(err, &se) && se.StatusCode == 401 { refreshToken(); retry() }\n    if se != nil && se.StatusCode == 429 { backoff(); retry() }\n}","preventionTips":["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"],"tags":["dingtalk","api","http-status"],"backgroundTag":"http-error-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}