{"record":{"id":"75e3aa7dc5311c08","repo":"chenhg5/cc-connect","slug":"dingtalk-send-image-request-w","errorCode":null,"errorMessage":"dingtalk: send image request: %w","messagePattern":"dingtalk: send image request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":1061,"sourceCode":"\t}\n\n\tbody, err := json.Marshal(requestBody)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dingtalk: marshal image message: %w\", err)\n\t}\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost,\n\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)","sourceCodeStart":1043,"sourceCodeEnd":1079,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L1043-L1079","documentation":"Returned by SendImage when the actual HTTP POST to DingTalk's oToMessages batchSend API fails at the transport level (connection error, timeout, TLS failure). It wraps the underlying net/http error, so the root cause is in the wrapped message.","triggerScenarios":"Network outage or DNS failure reaching api.dingtalk.com; request timeout from p.httpClient; TLS interception/proxy issues; context cancelled mid-request.","commonSituations":"Corporate proxy blocking api.dingtalk.com; HTTP client timeout too small for large deployments; container losing egress; DingTalk API regional outage.","solutions":["Inspect the wrapped error for the transport cause (dial timeout, TLS, DNS)","Verify egress connectivity: curl https://api.dingtalk.com from the host","Increase p.httpClient timeout if requests are large or the network is slow","Retry with backoff on transient errors (net.Error, timeouts); check DingTalk status page during outages"],"exampleFix":"// before\ncfg.HTTPClient.Timeout = 2 * time.Second\n// after\ncfg.HTTPClient.Timeout = 15 * time.Second // allow for slow DingTalk API responses","handlingStrategy":"retry","validationCode":"conn, err := net.DialTimeout(\"tcp\", \"api.dingtalk.com:443\", 5*time.Second)\nif err != nil {\n    return fmt.Errorf(\"no egress to api.dingtalk.com: %w\", err)\n}\nconn.Close()","typeGuard":null,"tryCatchPattern":"var ne net.Error\nif err := p.SendImage(ctx, rc, img); err != nil {\n    if errors.As(err, &ne) && ne.Timeout() {\n        // transient: retry with backoff\n    }\n    return err\n}","preventionTips":["Configure a sane HTTP client timeout (10-30s)","Retry transient transport errors with exponential backoff","Monitor host egress/DNS health; alert on api.dingtalk.com unreachable"],"tags":["network","http","dingtalk","timeout"],"backgroundTag":"http-request-failed","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"}