{"record":{"id":"ce3aff49d29a840e","repo":"router-for-me/CLIProxyAPI","slug":"antigravity-userinfo-missing-access-token","errorCode":null,"errorMessage":"antigravity userinfo: missing access token","messagePattern":"antigravity userinfo: missing access token","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/antigravity/auth.go","lineNumber":185,"sourceCode":"\t\tbody := strings.TrimSpace(string(bodyBytes))\n\t\tif body == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"antigravity token exchange: request failed: status %d\", resp.StatusCode)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"antigravity token exchange: request failed: status %d: %s\", resp.StatusCode, body)\n\t}\n\n\tvar token TokenResponse\n\tif errDecode := json.NewDecoder(resp.Body).Decode(&token); errDecode != nil {\n\t\treturn nil, fmt.Errorf(\"antigravity token exchange: decode response: %w\", errDecode)\n\t}\n\treturn &token, nil\n}\n\n// FetchUserInfo retrieves user email from Google\nfunc (o *AntigravityAuth) FetchUserInfo(ctx context.Context, accessToken string) (string, error) {\n\taccessToken = strings.TrimSpace(accessToken)\n\tif accessToken == \"\" {\n\t\treturn \"\", fmt.Errorf(\"antigravity userinfo: missing access token\")\n\t}\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, UserInfoEndpoint, nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"antigravity userinfo: create request: %w\", err)\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+accessToken)\n\treq.Header.Set(\"User-Agent\", o.shortUserAgent())\n\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","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/antigravity/auth.go#L167-L203","documentation":"FetchUserInfo was called with an access token that is empty after trimming whitespace. This is a caller-side validation failure before any HTTP request is made — the token passed in was \"\", whitespace-only, or never populated.","triggerScenarios":"Calling FetchUserInfo with an empty string; passing TokenResponse.AccessToken from a zero-value struct; token field name typo (e.g. accessToken vs access_token) leaving the variable empty.","commonSituations":"Calling userinfo before completing the token exchange; nil-check missed on the TokenResponse pointer and reading its zero-value field; a test calling with an unset fixture value.","solutions":["Complete ExchangeCodeForTokens first and check its error before fetching user info","Verify the field you pass actually carries the token (log its length, never the value)","Add a guard at the call site to skip the userinfo step when the token is empty"],"exampleFix":"// before\nemail, err := auth.FetchUserInfo(ctx, token.AccessToken)\n\n// after\nif token == nil || strings.TrimSpace(token.AccessToken) == \"\" {\n\treturn errors.New(\"cannot fetch userinfo: no access token\")\n}\nemail, err := auth.FetchUserInfo(ctx, token.AccessToken)","handlingStrategy":"validation","validationCode":"if token == nil || strings.TrimSpace(token.AccessToken) == \"\" {\n\treturn errors.New(\"skip userinfo: no access token\")\n}\nemail, err := auth.FetchUserInfo(ctx, token.AccessToken)","typeGuard":"func hasAccessToken(t *TokenResponse) bool {\n\treturn t != nil && strings.TrimSpace(t.AccessToken) != \"\"\n}","tryCatchPattern":null,"preventionTips":["Check the exchange result before any userinfo call","Guard zero-value structs at the call site"],"tags":["oauth","antigravity","validation","access-token"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}