{"record":{"id":"4b7f6d55003ce60b","repo":"router-for-me/CLIProxyAPI","slug":"fetch-claude-oauth-s-http-client-is-nil","errorCode":null,"errorMessage":"fetch Claude OAuth %s: HTTP client is nil","messagePattern":"fetch Claude OAuth (.+?): HTTP client is nil","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/claude/anthropic_auth.go","lineNumber":227,"sourceCode":"}\n\nfunc applyClaudeOAuthAxiosHeaders(req *http.Request) {\n\tif req == nil {\n\t\treturn\n\t}\n\treq.Header.Set(\"Accept\", \"application/json, text/plain, */*\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"User-Agent\", \"axios/1.15.2\")\n\treq.Header.Set(\"Accept-Encoding\", \"gzip, compress, deflate, br\")\n\treq.Header.Set(\"Connection\", \"close\")\n\treq.Close = true\n}\n\n// fetchOAuthControlPlaneJSON issues an Axios-shaped OAuth control-plane GET and\n// returns the decoded response body. label names the endpoint in error text.\nfunc (o *ClaudeAuth) fetchOAuthControlPlaneJSON(ctx context.Context, endpoint, accessToken, label string) ([]byte, error) {\n\tif o == nil || o.httpClient == nil {\n\t\treturn nil, fmt.Errorf(\"fetch Claude OAuth %s: HTTP client is nil\", label)\n\t}\n\taccessToken = strings.TrimSpace(accessToken)\n\tif accessToken == \"\" {\n\t\treturn nil, fmt.Errorf(\"fetch Claude OAuth %s: access token is empty\", label)\n\t}\n\treq, errRequest := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)\n\tif errRequest != nil {\n\t\treturn nil, fmt.Errorf(\"create Claude OAuth %s request: %w\", label, errRequest)\n\t}\n\tapplyClaudeOAuthAxiosHeaders(req)\n\treq.Header.Set(\"Authorization\", \"Bearer \"+accessToken)\n\treq.Header.Set(\"Cache-Control\", \"no-cache\")\n\n\tresp, errDo := o.httpClient.Do(req)\n\tif errDo != nil {\n\t\treturn nil, fmt.Errorf(\"fetch Claude OAuth %s: %w\", label, errDo)\n\t}\n\tdefer func() {","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/claude/anthropic_auth.go#L209-L245","documentation":"Returned by ClaudeAuth.fetchOAuthControlPlaneJSON when the method is invoked on a nil *ClaudeAuth receiver or when the struct's httpClient field is nil. Every OAuth control-plane GET (profile at api.anthropic.com/api/oauth/profile, roles) funnels through this helper, so a broken construction path disables all of them. ClaudeAuth is expected to be built by its constructor which always sets httpClient, so in practice this indicates the struct was created as &ClaudeAuth{} or the receiver was nil at the call site.","triggerScenarios":"Calling FetchOAuthProfile/FetchOAuthRoles on a ClaudeAuth built via struct literal without httpClient; invoking the method through a nil *ClaudeAuth pointer (Go allows the call, the guard catches it).","commonSituations":"Tests constructing ClaudeAuth{} directly instead of the constructor; refactors that store the auth in an interface whose nil concrete type is then dereferenced.","solutions":["Construct ClaudeAuth only through its constructor (NewClaudeAuth or equivalent) so httpClient is always initialized","If a struct literal is required, set the httpClient field explicitly: &ClaudeAuth{httpClient: &http.Client{}}","Check the label in the message ('profile' vs 'roles') to find which call path has the nil receiver"],"exampleFix":"// before\nauth := &ClaudeAuth{} // httpClient is nil\nprofile, err := auth.FetchOAuthProfile(ctx, token)\n\n// after\nauth := NewClaudeAuth(cfg) // httpClient initialized\nprofile, err := auth.FetchOAuthProfile(ctx, token)","handlingStrategy":"type-guard","validationCode":"if auth == nil {\n    return fmt.Errorf(\"ClaudeAuth is nil; construct via NewClaudeAuth\")\n}","typeGuard":"func claudeAuthReady(a *ClaudeAuth) bool {\n    return a != nil && a.httpClient != nil\n}","tryCatchPattern":"if !claudeAuthReady(auth) { return fmt.Errorf(\"auth not initialized\") }\nprofile, err := auth.FetchOAuthProfile(ctx, token)\nif err != nil && strings.Contains(err.Error(), \"HTTP client is nil\") {\n    log.Errorf(\"programming error: ClaudeAuth built without constructor\")\n}","preventionTips":["Always construct ClaudeAuth through its constructor so httpClient is set","Never build ClaudeAuth via bare struct literals in tests or refactors"],"tags":["claude","oauth","nil-guard","programming-error"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}