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
- 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
- Retry the request; body-close failures on keep-alive connections are typically transient
- 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
- Focus on the embedded HTTP status and body snippet — the close-body clause is secondary
- Retry transient failures; these usually stem from unstable connections
- Audit proxy/keep-alive settings if close errors appear repeatedly
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
- %s: status %d: %s
- usage endpoint returned status %d: %s
- reasonix: POST %s returned %d: %s
- gitee API returned HTTP %d
- http %d: %s
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/46b295b21a406b1d.
Report an issue: GitHub.