{"record":{"id":"514a242b81038f2f","repo":"router-for-me/CLIProxyAPI","slug":"antigravity-userinfo-request-failed-status-d","errorCode":null,"errorMessage":"antigravity userinfo: request failed: status %d","messagePattern":"antigravity userinfo: request failed: status (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/antigravity/auth.go","lineNumber":211,"sourceCode":"\n\tresp, errDo := o.httpClient.Do(req)\n\tif errDo != nil {\n\t\treturn \"\", fmt.Errorf(\"antigravity userinfo: execute request: %w\", errDo)\n\t}\n\tdefer func() {\n\t\tif errClose := resp.Body.Close(); errClose != nil {\n\t\t\tlog.Errorf(\"antigravity userinfo: close body error: %v\", errClose)\n\t\t}\n\t}()\n\n\tif resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {\n\t\tbodyBytes, errRead := io.ReadAll(io.LimitReader(resp.Body, 8<<10))\n\t\tif errRead != nil {\n\t\t\treturn \"\", fmt.Errorf(\"antigravity userinfo: read response: %w\", errRead)\n\t\t}\n\t\tbody := strings.TrimSpace(string(bodyBytes))\n\t\tif body == \"\" {\n\t\t\treturn \"\", fmt.Errorf(\"antigravity userinfo: request failed: status %d\", resp.StatusCode)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"antigravity userinfo: request failed: status %d: %s\", resp.StatusCode, body)\n\t}\n\tvar info userInfo\n\tif errDecode := json.NewDecoder(resp.Body).Decode(&info); errDecode != nil {\n\t\treturn \"\", fmt.Errorf(\"antigravity userinfo: decode response: %w\", errDecode)\n\t}\n\temail := strings.TrimSpace(info.Email)\n\tif email == \"\" {\n\t\treturn \"\", fmt.Errorf(\"antigravity userinfo: response missing email\")\n\t}\n\treturn email, nil\n}\n\n// FetchProjectID retrieves the project ID for the authenticated user via loadCodeAssist\nfunc (o *AntigravityAuth) FetchProjectID(ctx context.Context, accessToken string) (string, error) {\n\tuserAgent := o.shortUserAgent()\n\tloadReqBody := map[string]any{","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/antigravity/auth.go#L193-L229","documentation":"The userinfo endpoint returned a non-2xx status with an empty body. Most commonly 401 (access token invalid, expired, or revoked) or 403 (insufficient scope for userinfo), occasionally a 5xx with no body.","triggerScenarios":"Calling FetchUserInfo with a token that expired or was revoked; using a token from a different flow/client; Google userinfo outage.","commonSituations":"Fetching user info long after the exchange completed; token stored and reused across process restarts after expiry; scopes not including email/profile.","solutions":["Retry with a freshly exchanged access token","If using a stored token, refresh it first via the refresh-token flow","Confirm the OAuth scopes requested during authorization include email access"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if time.Since(token.AcquiredAt) > time.Duration(token.ExpiresIn-30)*time.Second {\n\ttoken = refreshAccessToken(token.RefreshToken)\n}\nemail, err := auth.FetchUserInfo(ctx, token.AccessToken)","typeGuard":null,"tryCatchPattern":"if strings.Contains(err.Error(), \"userinfo: request failed: status 401\") {\n\t// refresh the token, then retry once\n}","preventionTips":["Fetch userinfo immediately after exchange","Honor expires_in and refresh before use","Request the email scope up front"],"tags":["oauth","antigravity","http-status","access-token"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}