{"record":{"id":"96c442001e44a589","repo":"router-for-me/CLIProxyAPI","slug":"xai-device-token-response-missing-access-token","errorCode":null,"errorMessage":"xai device token response missing access_token","messagePattern":"xai device token response missing access_token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/xai/xai.go","lineNumber":323,"sourceCode":"\t\t\treturn nil, nil, nextInterval, true\n\t\tcase \"expired_token\":\n\t\t\treturn nil, fmt.Errorf(\"xai device code expired\"), interval, false\n\t\tcase \"access_denied\":\n\t\t\treturn nil, fmt.Errorf(\"xai device authorization denied\"), interval, false\n\t\tdefault:\n\t\t\tdesc := strings.TrimSpace(payload.ErrorDescription)\n\t\t\tif desc != \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"xai device token error: %s: %s\", payload.Error, desc), interval, false\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"xai device token error: %s\", payload.Error), interval, false\n\t\t}\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"xai device token request failed with status %d: %s\", resp.StatusCode, strings.TrimSpace(string(body))), interval, false\n\t}\n\tif strings.TrimSpace(payload.AccessToken) == \"\" {\n\t\treturn nil, fmt.Errorf(\"xai device token response missing access_token\"), interval, false\n\t}\n\n\temail, subject := parseJWTIdentity(payload.IDToken)\n\treturn buildTokenData(payload.AccessToken, payload.RefreshToken, payload.IDToken, payload.TokenType, payload.ExpiresIn, email, subject), nil, interval, false\n}\n\n// RefreshTokens refreshes an xAI access token.\nfunc (a *XAIAuth) RefreshTokens(ctx context.Context, refreshToken, tokenEndpoint string) (*TokenData, error) {\n\tif strings.TrimSpace(refreshToken) == \"\" {\n\t\treturn nil, fmt.Errorf(\"xai token refresh: refresh token is required\")\n\t}\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\trefreshToken = strings.TrimSpace(refreshToken)\n\tif strings.TrimSpace(tokenEndpoint) == \"\" {\n\t\tdiscovery, errDiscover := a.Discover(ctx)\n\t\tif errDiscover != nil {","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/xai/xai.go#L305-L341","documentation":"The exchange technically succeeded (no error field, status 200) but access_token is empty, which the client treats as a hard failure since TokenData cannot be built without it. This indicates a malformed or surprising success response from the token endpoint.","triggerScenarios":"200 JSON from the token endpoint lacking access_token — e.g. {\"token_type\":\"Bearer\"} or an unrelated JSON object.","commonSituations":"Wrong endpoint that returns 200 JSON for everything; response schema change by xAI; a proxy returning a success-shaped health-check body.","solutions":["Log the full body to see what the 'success' response actually contained","Confirm via discovery that the token endpoint is the real OAuth token URL","If xAI changed field names, update the payload struct json tags in exchangeDeviceCode"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"missing access_token\") {\n    log.Errorf(\"token endpoint returned 200 without access_token; verify endpoint URL and xAI schema: %v\", err)\n    return err\n}","preventionTips":["Use only discovery-derived token endpoints","Contract-test the token response shape after library or provider upgrades"],"tags":["oauth","xai","auth","response-validation"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}