{"record":{"id":"fc6eac90db72281c","repo":"chenhg5/cc-connect","slug":"dingtalk-send-audio-failed-status-d-body-s","errorCode":null,"errorMessage":"dingtalk: send audio failed: status=%d, body=%s","messagePattern":"dingtalk: send audio failed: status=(.+?), body=(.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":1289,"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 audio 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 audio request: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tslog.Debug(\"dingtalk: oToMessages API response\", \"status\", resp.StatusCode, \"body\", string(respBody))\n\n\tif resp.StatusCode != 200 {\n\t\treturn fmt.Errorf(\"dingtalk: send audio failed: status=%d, body=%s\", resp.StatusCode, string(respBody))\n\t}\n\n\tslog.Info(\"dingtalk: voice message sent successfully\", \"media_id\", mediaID, \"conversation_id\", rc.conversationId)\n\treturn nil\n}\n\n// compressAudio compresses audio if it exceeds size limits.\n// Uses ffmpeg to convert WAV to MP3 format (DingTalk supported, ~10:1 compression ratio).\nfunc (p *Platform) compressAudio(ctx context.Context, audio []byte, format string) ([]byte, string, error) {\n\t// Only WAV format can be compressed to MP3\n\tif strings.ToLower(format) != \"wav\" {\n\t\treturn nil, \"\", fmt.Errorf(\"only WAV format can be compressed, got: %s\", format)\n\t}\n\n\treturn p.compressAudioWithFFmpeg(ctx, audio, format)\n}\n\n// compressAudioWithFFmpeg compresses audio using ffmpeg with stdin/stdout pipes.","sourceCodeStart":1271,"sourceCodeEnd":1307,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L1271-L1307","documentation":"This error is returned when the DingTalk oToMessages batch-send HTTP API responds with a status code other than 200 while sending a voice/audio message. The library treats any non-200 response as a delivery failure and surfaces the raw status and response body so the caller can diagnose the API-side problem. The body usually contains DingTalk's errcode/errmsg explaining the rejection.","triggerScenarios":"Sending an audio voice message via p.sendAudio (oToMessages API) where the POST returns 400/401/403/500 — e.g. an invalid or expired access token embedded in the media_id flow, an invalid chatCode/cid, or a media_id that was rejected.","commonSituations":"Expired or wrong appKey/appSecret credentials producing invalid tokens; sending to a conversationId the bot is not a member of; media upload succeeded but the media_id was for the wrong media type; DingTalk-side rate limiting or transient 5xx.","solutions":["Read the body string in the error for DingTalk's errcode/errmsg and address that specific cause (e.g. errcode 40001 → refresh access token).","Verify appKey/appSecret in config.toml and that the bot is a member of the target conversation.","Re-upload the media and retry once — the media_id may have expired or the failure was transient (5xx).","Confirm the target conversationId/chatCode matches an existing group or single chat the bot can message."],"exampleFix":"// before: token cached forever, may expire\naccessToken := p.accessToken\n// after: refresh when API returns invalid-token errcode\nif strings.Contains(respBody, \"40001\") { _ = p.refreshAccessToken(); return p.sendAudio(...) }","handlingStrategy":"try-catch","validationCode":"// Go: check token and conversation before sending\nif token == \"\" { return errors.New(\"no access token\") }\nif !botInConversation(cid) { return fmt.Errorf(\"bot not member of %s\", cid) }","typeGuard":null,"tryCatchPattern":"if err := p.sendAudio(ctx, cid, mediaID); err != nil {\n    var apiErr *APIError\n    if errors.As(err, &apiErr) { slog.Warn(\"dingtalk audio rejected\", \"body\", apiErr.Body) }\n    // retry once on 5xx, refresh token on 401/40001\n}","preventionTips":["Parse DingTalk's errcode/errmsg from the response body on every failure.","Refresh the access token on 401/errcode 40001 instead of failing hard.","Verify bot membership in the target conversation during setup, not at send time."],"tags":["dingtalk","http","audio","api-response"],"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"}