router-for-me/CLIProxyAPI · error
antigravity token exchange: read response: %w
Error message
antigravity token exchange: read response: %w
What it means
The token exchange returned a non-2xx status and reading the (limited to 8 KiB) error response body failed. The server responded with an error status but the body read was interrupted — connection reset mid-body, or the context was cancelled while reading.
Source
Thrown at internal/auth/antigravity/auth.go:165
if err != nil {
return nil, fmt.Errorf("antigravity token exchange: create request: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, errDo := o.httpClient.Do(req)
if errDo != nil {
return nil, fmt.Errorf("antigravity token exchange: execute request: %w", errDo)
}
defer func() {
if errClose := resp.Body.Close(); errClose != nil {
log.Errorf("antigravity token exchange: close body error: %v", errClose)
}
}()
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
bodyBytes, errRead := io.ReadAll(io.LimitReader(resp.Body, 8<<10))
if errRead != nil {
return nil, fmt.Errorf("antigravity token exchange: read response: %w", errRead)
}
body := strings.TrimSpace(string(bodyBytes))
if body == "" {
return nil, fmt.Errorf("antigravity token exchange: request failed: status %d", resp.StatusCode)
}
return nil, fmt.Errorf("antigravity token exchange: request failed: status %d: %s", resp.StatusCode, body)
}
var token TokenResponse
if errDecode := json.NewDecoder(resp.Body).Decode(&token); errDecode != nil {
return nil, fmt.Errorf("antigravity token exchange: decode response: %w", errDecode)
}
return &token, nil
}
// FetchUserInfo retrieves user email from Google
func (o *AntigravityAuth) FetchUserInfo(ctx context.Context, accessToken string) (string, error) {
accessToken = strings.TrimSpace(accessToken)View on GitHub (pinned to 78f0c4079e)
Solutions
- Increase the context/httpClient timeout so body reads are not cut off after headers arrive
- Retry the request; transient resets usually succeed on retry (but note the auth code single-use constraint)
- If persistent, capture resp.StatusCode in the error to know what the server was trying to report
Example fix
// before
if errRead != nil {
return nil, fmt.Errorf("antigravity token exchange: read response: %w", errRead)
}
// after
if errRead != nil {
return nil, fmt.Errorf("antigravity token exchange: read response (status %d): %w", resp.StatusCode, errRead)
} Defensive patterns
Strategy: retry
Try / catch
if err != nil && strings.Contains(err.Error(), "read response") {
// body read interrupted; status unknown — obtain a new auth code and retry once
} Prevention
- Size timeouts to include body reads, not just headers
- Treat partial error bodies as retryable only with a fresh code
When it happens
Trigger: Upstream returns 4xx/5xx and closes the connection before the body completes; context deadline expires between response headers and body read; proxy truncates the error body.
Common situations: Aggressive per-request timeouts set just above the header round-trip; flaky networks; rate-limiting endpoints that RST connections.
Related errors
- antigravity userinfo: read response: %w
- antigravity token exchange: create request: %w
- antigravity token exchange: execute request: %w
- antigravity token exchange: decode response: %w
- antigravity userinfo: execute request: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/77ac688656c639e9.
Report an issue: GitHub.