chenhg5/cc-connect · error

%s: status %d: %s (close body: %v)

Error message

%s: status %d: %s (close body: %v)

What it means

httpErrorBody builds the error returned for non-2xx Google Chat API responses. It reads up to 2048 bytes of the response body for diagnostics; if closing the response body itself fails, the close error is appended in a '(close body: %v)' clause so neither detail is lost. This variant is rare and indicates an I/O problem while releasing the HTTP connection.

Source

Thrown at platform/googlechat/googlechat.go:356

		return nil, fmt.Errorf("googlechat: invalid session key %q", sessionKey)
	}
	if idx := strings.Index(rest, threadSep); idx != -1 {
		return replyContext{space: rest[:idx], thread: rest[idx+len(threadSep):]}, nil
	}
	// User-scoped keys append ":<user>" where user is "users/<id>"; strip a
	// trailing "users/..." segment to recover the bare space.
	if idx := strings.LastIndex(rest, ":users/"); idx != -1 {
		return replyContext{space: rest[:idx]}, nil
	}
	return replyContext{space: rest}, nil
}

// httpErrorBody reads up to 2048 bytes from resp.Body, closes it, and returns
// an error combining prefix, status code, and the response snippet.
func httpErrorBody(resp *http.Response, prefix string) error {
	b, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
	if err := resp.Body.Close(); err != nil {
		return fmt.Errorf("%s: status %d: %s (close body: %v)", prefix, resp.StatusCode, strings.TrimSpace(string(b)), err)
	}
	return fmt.Errorf("%s: status %d: %s", prefix, resp.StatusCode, strings.TrimSpace(string(b)))
}

// coalesce returns s if non-empty, otherwise def.
func coalesce(s, def string) string {
	if s != "" {
		return s
	}
	return def
}

// messageURL returns the Chat messages endpoint for rc's space, appending the
// messageReplyOption query when a thread is known.
func messageURL(rc replyContext) string {
	u := chatAPIBase + rc.space + "/messages"
	if rc.thread != "" {
		u += "?messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD"

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the embedded status code and body snippet first — that is the real failure (e.g. 403 permission, 404 space) and fix that underlying API issue
  2. Retry the request; body-close failures on keep-alive connections are typically transient
  3. Disable aggressive HTTP keep-alive/proxy settings if close errors recur
Defensive patterns

Strategy: try-catch

Try / catch

if err := send(); err != nil {
    if strings.Contains(err.Error(), "close body") {
        slog.Warn("chat api call failed and body close also failed; check status snippet", "err", err)
    }
    return err
}

Prevention

When it happens

Trigger: doRequest receives a non-2xx response from the Chat API, and the subsequent resp.Body.Close() call fails (e.g. connection reset while closing, already-corrupted connection).

Common situations: Unstable networks or proxies interrupting keep-alive connections; HTTP client/connection pool issues; usually accompanied by a more meaningful status code and body snippet in the same message.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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