{"record":{"id":"eff078094aeb7bfe","repo":"chenhg5/cc-connect","slug":"dingtalk-send-image-failed-status-d-body-s","errorCode":null,"errorMessage":"dingtalk: send image failed: status=%d, body=%s","messagePattern":"dingtalk: send image failed: status=(.+?), body=(.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":1069,"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 image 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 image request: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tslog.Debug(\"dingtalk: oToMessages image response\", \"status\", resp.StatusCode, \"body\", string(respBody))\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"dingtalk: send image failed: status=%d, body=%s\", resp.StatusCode, string(respBody))\n\t}\n\n\tslog.Info(\"dingtalk: image message sent\", \"media_id\", mediaID, \"user\", rc.senderStaffId)\n\treturn nil\n}\n\nvar _ core.ImageSender = (*Platform)(nil)\nvar _ core.StreamingCardPlatform = (*Platform)(nil)\nvar _ core.ReplyContextReconstructor = (*Platform)(nil)\nvar _ core.TypingIndicator = (*Platform)(nil)\nvar _ core.TypingIndicatorDone = (*Platform)(nil)\n\n// CreateStreamingCard creates a new streaming card for the given reply context.\n// Implements core.StreamingCardPlatform.\nfunc (p *Platform) CreateStreamingCard(ctx context.Context, replyCtx any) (core.StreamingCard, error) {\n\tif p.cardTemplateID == \"\" {\n\t\treturn nil, fmt.Errorf(\"dingtalk: card_template_id not configured\")\n\t}","sourceCodeStart":1051,"sourceCodeEnd":1087,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L1051-L1087","documentation":"Returned by SendImage when DingTalk's oToMessages batchSend API replies with a non-200 status. The response body (DingTalk's JSON error payload) is embedded in the error, so read it to identify the API-level cause such as an expired access token or invalid robot code/user id.","triggerScenarios":"Expired or invalid access token (401/400); senderStaffId not reachable by this robot; robot app lacking permission for oToMessages; mediaID invalid or expired; DingTalk rate limiting (429).","commonSituations":"Access token cache expired; image uploaded to wrong media type; target user never messaged the robot first (oToMessages requires existing contact); app permissions not granted in DingTalk developer console.","solutions":["Read the body=%s portion of the error — it contains DingTalk's code/message","If 401/invalidAuthentication, force token refresh (clear cached access token) and retry","Verify the robot app has the 'Enterprise robot message' permission and the target userId is correct","Ensure mediaID came from a successful uploadMedia with mediaType 'image'","Retry with backoff if status is 429/5xx"],"exampleFix":"// before\nif resp.StatusCode != http.StatusOK {\n    return fmt.Errorf(\"dingtalk: send image failed: status=%d, body=%s\", resp.StatusCode, string(respBody))\n}\n// after (caller-side)\nif err := p.SendImage(ctx, rc, img); err != nil && strings.Contains(err.Error(), \"invalidAuthentication\") {\n    p.invalidateToken()\n    err = p.SendImage(ctx, rc, img)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := p.SendImage(ctx, rc, img); err != nil {\n    var apiErr struct{ Status int; Body string }\n    if strings.Contains(err.Error(), \"invalidAuthentication\") {\n        p.invalidateToken(); /* retry once */\n    }\n    slog.Warn(\"dingtalk image send rejected\", \"err\", err)\n}","preventionTips":["Parse the body= payload for DingTalk's error code before deciding to retry","Refresh access tokens proactively before expiry","Confirm oToMessages permissions and recipient contact requirements in the DingTalk console"],"tags":["http","api","dingtalk","http-error-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"}