chenhg5/cc-connect · warning

googlechat: drain response body: %w

Error message

googlechat: drain response body: %w

What it means

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.

Source

Thrown at platform/googlechat/googlechat.go:436

	if rc.space == "" {
		return fmt.Errorf("googlechat: missing space in reply context")
	}
	url, body, err := buildSendRequest(rc, content)
	if err != nil {
		return err
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
	if err != nil {
		return fmt.Errorf("googlechat: build request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := p.doRequest(req)
	if err != nil {
		return err
	}
	if _, err := io.Copy(io.Discard, resp.Body); err != nil {
		_ = resp.Body.Close()
		return fmt.Errorf("googlechat: drain response body: %w", err)
	}
	if err := resp.Body.Close(); err != nil {
		return fmt.Errorf("googlechat: close response body: %w", err)
	}
	return nil
}

func (p *Platform) Reply(ctx context.Context, rctx any, content string) error {
	return p.post(ctx, rctx, content)
}

func (p *Platform) Send(ctx context.Context, rctx any, content string) error {
	return p.post(ctx, rctx, content)
}

// uploadAttachment uploads raw bytes to the Chat media endpoint using a
// multipart/related request and returns the attachmentDataRef resource name.
func (p *Platform) uploadAttachment(ctx context.Context, space, filename, mimeType string, data []byte) (string, error) {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Treat the send as delivered (status was 2xx) and log the drain error as a warning rather than re-sending, to avoid duplicate messages
  2. Retry on the next send; transient resets typically resolve on a fresh connection
  3. If frequent, disable HTTP keep-alive reuse or fix proxy idle-timeout settings between the host and googleapis.com

Example fix

// before
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
    return fmt.Errorf("googlechat: drain response body: %w", err) // treated as send failure
}
// after
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
    slog.Warn("googlechat: response drain failed; message likely delivered", "err", err)
    _ = resp.Body.Close()
    return nil
}
Defensive patterns

Strategy: try-catch

Try / catch

if err := p.post(ctx, rc, content); err != nil {
    if strings.Contains(err.Error(), "drain response body") {
        slog.Warn("googlechat: response drain failed after 2xx; treating send as delivered", "err", err)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: Flaky networks or aggressive proxies/LBs closing idle keep-alive connections; mobile/VPN link drops; Google-side connection resets under load.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/1b58ef31d5bd46ba. Report an issue: GitHub.