{"record":{"id":"eec1e699c315dd75","repo":"charmbracelet/crush","slug":"oauth-token-is-nil","errorCode":null,"errorMessage":"oauth token is nil","messagePattern":"oauth token is nil","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/config.go","lineNumber":99,"sourceCode":"// format tags the credential with an explicit Kind so the server can\n// decode it back into the right Go type — JSON's `any` loses that\n// information across the socket.\nfunc (c *Client) SetProviderAPIKey(ctx context.Context, id string, scope config.Scope, providerID string, apiKey any) error {\n\tvar (\n\t\tkind proto.APIKeyKind\n\t\traw  json.RawMessage\n\t)\n\tswitch v := apiKey.(type) {\n\tcase string:\n\t\tkind = proto.APIKeyKindString\n\t\tb, err := json.Marshal(v)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to marshal api key string: %w\", err)\n\t\t}\n\t\traw = b\n\tcase *oauth.Token:\n\t\tif v == nil {\n\t\t\treturn fmt.Errorf(\"oauth token is nil\")\n\t\t}\n\t\tkind = proto.APIKeyKindOAuth\n\t\tb, err := json.Marshal(v)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to marshal oauth token: %w\", err)\n\t\t}\n\t\traw = b\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported api key type %T\", apiKey)\n\t}\n\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/config/provider-key\", id), nil, jsonBody(proto.ConfigProviderKeyRequest{\n\t\tScope:      scope,\n\t\tProviderID: providerID,\n\t\tKind:       kind,\n\t\tAPIKey:     raw,\n\t}), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/config.go#L81-L117","documentation":"SetProviderAPIKey accepts *oauth.Token for OAuth providers. A typed-nil *oauth.Token passed as the apiKey carries no credential data, so the client rejects it before making any network request.","triggerScenarios":"Calling SetProviderAPIKey(ctx, wsID, scope, providerID, (*oauth.Token)(nil)) — the type switch matches *oauth.Token but v == nil.","commonSituations":"A token-loading function returned (nil, nil) and the caller forwarded the nil token without checking; a variable declared as *oauth.Token but never assigned.","solutions":["Check the OAuth token is non-nil before calling SetProviderAPIKey","If authentication failed, propagate that error instead of passing a nil token","Pass a string API key instead if the provider uses a plain key"],"exampleFix":"// before\ntok := loadToken() // may return nil\nclient.SetProviderAPIKey(ctx, wsID, scope, providerID, tok)\n// after\ntok := loadToken()\nif tok == nil {\n    return errors.New(\"no oauth token available\")\n}\nclient.SetProviderAPIKey(ctx, wsID, scope, providerID, tok)","handlingStrategy":"validation","validationCode":"if tok, ok := apiKey.(*oauth.Token); ok && tok == nil {\n    return errors.New(\"cannot set provider key: oauth token is nil\")\n}","typeGuard":"func isUsableKey(key any) bool {\n    switch v := key.(type) {\n    case string:\n        return v != \"\"\n    case *oauth.Token:\n        return v != nil\n    default:\n        return false\n    }\n}","tryCatchPattern":null,"preventionTips":["Always nil-check tokens returned from auth/login functions before use","Never forward (nil, nil) results from token loaders up the call chain"],"tags":["oauth","validation","nil"],"backgroundTag":"nil-oauth-token","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}