{"record":{"id":"d3aa0f336ae4a464","repo":"chenhg5/cc-connect","slug":"read-upload-response-w","errorCode":null,"errorMessage":"read upload response: %w","messagePattern":"read upload response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":1378,"sourceCode":"\t\treturn \"\", fmt.Errorf(\"close multipart writer: %w\", err)\n\t}\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, uploadURL, body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create upload request: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", writer.FormDataContentType())\n\n\tresp, err := p.httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"upload request: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\trespBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"read upload response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"upload returned status %d: %s\", resp.StatusCode, respBody)\n\t}\n\n\tslog.Debug(\"dingtalk: media upload response\", \"status\", resp.StatusCode, \"body\", string(respBody))\n\n\tvar uploadResp struct {\n\t\tErrCode int    `json:\"errcode\"`\n\t\tErrMsg  string `json:\"errmsg\"`\n\t\tMediaID string `json:\"media_id\"`\n\t\tType    string `json:\"type\"`\n\t}\n\tif err := json.Unmarshal(respBody, &uploadResp); err != nil {\n\t\treturn \"\", fmt.Errorf(\"decode upload response: %w, body: %s\", err, respBody)\n\t}\n","sourceCodeStart":1360,"sourceCodeEnd":1396,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L1360-L1396","documentation":"This error wraps an io.ReadAll failure that occurred while reading the body of the HTTP response from DingTalk's media upload endpoint. The upload request itself was sent successfully, but the response body could not be fully read, typically because the connection was interrupted or reset mid-transfer. It is thrown by the DingTalk platform's media upload helper (returning \"\", err) whenever the response body stream errors.","triggerScenarios":"Calling the media upload path (image/file/voice/video send) when io.ReadAll(resp.Body) returns a non-nil error — e.g. the TCP connection to DingTalk's upload host is closed or reset before the body completes, a proxy/VPN severs the stream, or a read deadline elapses partway through.","commonSituations":"Corporate proxies or firewalls killing long-running uploads; flaky mobile/office networks; very large media files whose download phase outlives a NAT or idle-timeout window; Docker/Kubernetes DNS or egress interruptions.","solutions":["Check network connectivity and any corporate proxy/VPN between the host and DingTalk's API (oapi.dingtalk.com / api.dingtalk.com).","Retry the send — the underlying error is often transient, so a subsequent upload attempt may succeed.","Reduce the media file size or re-encode it so the response window is shorter.","If a custom HTTP client/transport is used, verify read timeouts are not too aggressive for large uploads.","Capture the wrapped error (%w) with errors.Unwrap or errors.Is to identify the exact net/http root cause (connection reset, EOF, timeout)."],"exampleFix":"// before\nrespBody, err := io.ReadAll(resp.Body)\nif err != nil {\n    return \"\", fmt.Errorf(\"read upload response: %w\", err)\n}\n// after\nrespBody, err := io.ReadAll(resp.Body)\nif err != nil {\n    slog.Warn(\"dingtalk: media upload response read failed, will surface to caller\", \"err\", err)\n    return \"\", fmt.Errorf(\"read upload response: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// precondition check before upload\nif len(data) == 0 {\n    return fmt.Errorf(\"nothing to upload\")\n}\n// ensure client has sane timeout\nclient := &http.Client{Timeout: 60 * time.Second}","typeGuard":"func isTransientReadErr(err error) bool {\n    var ne net.Error\n    return errors.As(err, &ne) || errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)\n}","tryCatchPattern":"mediaID, err := uploadMedia(ctx, data, mediaType)\nif err != nil {\n    if isTransientReadErr(errors.Unwrap(err)) {\n        // retry once after backoff\n        time.Sleep(2 * time.Second)\n        mediaID, err = uploadMedia(ctx, data, mediaType)\n    }\n    if err != nil {\n        slog.Error(\"media upload failed reading response\", \"err\", err)\n        return err\n    }\n}","preventionTips":["Set a generous but bounded http.Client timeout for uploads.","Run cc-connect on a host with stable egress; avoid flaky VPNs for production.","Keep media files small to shorten the network window.","Monitor logs for repeated read errors indicating infra-level network problems."],"tags":["network","http","io","upload","dingtalk"],"backgroundTag":"network-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"}