{"record":{"id":"1b58ef31d5bd46ba","repo":"chenhg5/cc-connect","slug":"googlechat-drain-response-body-w","errorCode":null,"errorMessage":"googlechat: drain response body: %w","messagePattern":"googlechat: drain response body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"platform/googlechat/googlechat.go","lineNumber":436,"sourceCode":"\tif rc.space == \"\" {\n\t\treturn fmt.Errorf(\"googlechat: missing space in reply context\")\n\t}\n\turl, body, err := buildSendRequest(rc, content)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"googlechat: build request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, err := p.doRequest(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err := io.Copy(io.Discard, resp.Body); err != nil {\n\t\t_ = resp.Body.Close()\n\t\treturn fmt.Errorf(\"googlechat: drain response body: %w\", err)\n\t}\n\tif err := resp.Body.Close(); err != nil {\n\t\treturn fmt.Errorf(\"googlechat: close response body: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (p *Platform) Reply(ctx context.Context, rctx any, content string) error {\n\treturn p.post(ctx, rctx, content)\n}\n\nfunc (p *Platform) Send(ctx context.Context, rctx any, content string) error {\n\treturn p.post(ctx, rctx, content)\n}\n\n// uploadAttachment uploads raw bytes to the Chat media endpoint using a\n// multipart/related request and returns the attachmentDataRef resource name.\nfunc (p *Platform) uploadAttachment(ctx context.Context, space, filename, mimeType string, data []byte) (string, error) {","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/googlechat/googlechat.go#L418-L454","documentation":"After a successful 2xx Chat API response, post drains the response body (io.Copy to io.Discard) so the HTTP connection can be reused, and wraps any drain failure with this error. It indicates the connection broke mid-read even though the request itself was accepted — the message was very likely delivered.","triggerScenarios":"Reading the 2xx response body fails due to connection reset, TLS truncation, proxy timeouts, or keep-alive connections closed by the server/proxy before the body finished arriving.","commonSituations":"Flaky networks or aggressive proxies/LBs closing idle keep-alive connections; mobile/VPN link drops; Google-side connection resets under load.","solutions":["Treat the send as delivered (status was 2xx) and log the drain error as a warning rather than re-sending, to avoid duplicate messages","Retry on the next send; transient resets typically resolve on a fresh connection","If frequent, disable HTTP keep-alive reuse or fix proxy idle-timeout settings between the host and googleapis.com"],"exampleFix":"// before\nif _, err := io.Copy(io.Discard, resp.Body); err != nil {\n    return fmt.Errorf(\"googlechat: drain response body: %w\", err) // treated as send failure\n}\n// after\nif _, err := io.Copy(io.Discard, resp.Body); err != nil {\n    slog.Warn(\"googlechat: response drain failed; message likely delivered\", \"err\", err)\n    _ = resp.Body.Close()\n    return nil\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := p.post(ctx, rc, content); err != nil {\n    if strings.Contains(err.Error(), \"drain response body\") {\n        slog.Warn(\"googlechat: response drain failed after 2xx; treating send as delivered\", \"err\", err)\n        return nil\n    }\n    return err\n}","preventionTips":["Never auto-retry sends that reached a 2xx status — dedupe on message side instead","Configure proxy/load-balancer idle timeouts above the client's keep-alive interval","Monitor drain errors as a network-health signal; escalate if frequent"],"tags":["googlechat","http","network"],"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-14T00:17:10.932Z"}