router-for-me/CLIProxyAPI · error
antigravity token exchange: request failed: status %d: %s
Error message
antigravity token exchange: request failed: status %d: %s
What it means
The Antigravity token exchange endpoint returned a non-2xx status and the body (up to 8 KiB) is included verbatim. Google OAuth errors are JSON like {"error":"invalid_grant","error_description":"..."}; the body tells you the exact protocol failure.
Source
Thrown at internal/auth/antigravity/auth.go:171
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)
if accessToken == "" {
return "", fmt.Errorf("antigravity userinfo: missing access token")
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, UserInfoEndpoint, nil)
if err != nil {
return "", fmt.Errorf("antigravity userinfo: create request: %w", err)View on GitHub (pinned to 78f0c4079e)
Solutions
- Read the error/error_description fields in the body: invalid_grant means restart the flow with a fresh code; invalid_client means fix credentials; redirect_uri_mismatch means align the redirect URI
- For clock-skew-induced invalid_grant, sync system time (NTP)
- Ensure the code is exchanged exactly once, immediately after the callback
Defensive patterns
Strategy: try-catch
Try / catch
if strings.Contains(err.Error(), "token exchange: request failed") {
switch {
case strings.Contains(err.Error(), "invalid_grant"): // fresh code needed
case strings.Contains(err.Error(), "invalid_client"): // fix credentials
case strings.Contains(err.Error(), "redirect_uri_mismatch"): // align redirect
}
} Prevention
- Read error/error_description from the body to route the fix
- Keep client credentials in sync with the registered OAuth app
- Sync system clock (NTP) to avoid invalid_grant from skew
When it happens
Trigger: invalid_grant (expired/used code, clock skew), invalid_client (bad client secret), redirect_uri_mismatch, unauthorized_client — each surfaces as 400/401 with a JSON error body.
Common situations: Replaying a used auth code during debugging; expired refresh-token flow bugs; misconfigured OAuth client credentials in a forked build.
Related errors
- antigravity userinfo: request failed: status %d: %s
- antigravity token exchange: request failed: status %d
- antigravity userinfo: request failed: status %d
- request failed with status %d: %s
- antigravity token exchange: create request: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/e5b126e95a7a1388.
Report an issue: GitHub.