{"record":{"id":"5e5bafc0fe09294e","repo":"charmbracelet/crush","slug":"failed-to-get-config-w","errorCode":null,"errorMessage":"failed to get config: %w","messagePattern":"failed to get config: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/logout.go","lineNumber":126,"sourceCode":"\tctx := getLogoutContext()\n\n\tif err := cmp.Or(\n\t\tc.RemoveConfigField(ctx, wsID, config.ScopeGlobal, \"providers.copilot.api_key\"),\n\t\tc.RemoveConfigField(ctx, wsID, config.ScopeGlobal, \"providers.copilot.oauth\"),\n\t); err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Println(logoutHeaderStyle.Render(\"Successfully logged out of GitHub Copilot.\"))\n\treturn nil\n}\n\nfunc pickLoggedInProvider(c *client.Client, wsID string) (string, error) {\n\tctx := getLogoutContext()\n\n\tcfg, err := c.GetConfig(ctx, wsID)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to get config: %w\", err)\n\t}\n\n\ttype loggedInProvider struct {\n\t\tid   string\n\t\tname string\n\t}\n\n\t// Only OAuth-based providers support login/logout. Keep this list in sync\n\t// with the switch in RunE and the login command.\n\toauthProviders := map[string]string{\n\t\t\"hyper\":   \"Hyper\",\n\t\t\"copilot\": \"GitHub Copilot\",\n\t}\n\n\tvar loggedIn []loggedInProvider\n\tfor id, name := range oauthProviders {\n\t\tif p, ok := cfg.Providers.Get(id); ok && p.OAuthToken != nil {\n\t\t\tloggedIn = append(loggedIn, loggedInProvider{id: id, name: name})","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/cmd/logout.go#L108-L144","documentation":"pickLoggedInProvider fetches the workspace configuration via client.GetConfig to enumerate logged-in providers for the interactive logout picker. If that RPC fails, the error is wrapped with this message. It is a wrapper around the transport or server-side failure of the config endpoint, preserving the cause with %w.","triggerScenarios":"Calling the logout flow (anonymous caller path) when GetConfig fails: server unreachable, workspace id invalid, authentication rejected, or the context returned by getLogoutContext is already canceled/timed out.","commonSituations":"Daemon not running when invoking `crush logout`; expired auth token causing 401 on the config endpoint; stale workspace id after workspace deletion; network offline.","solutions":["Unwrap the error to see the root cause (transport vs status).","Ensure the crush server/daemon is running and you are authenticated (try a cheap command like `crush models`).","Verify the workspace id is valid; re-login if credentials expired.","Retry the logout command once the server is reachable."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Go: confirm server reachable before logout flow\nif _, err := c.ListWorkspaces(ctx); err != nil {\n    return fmt.Errorf(\"cannot reach server for logout: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"provider, err := pickLoggedInProvider(c, wsID)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to get config\") {\n        // surface the wrapped cause and prompt re-login\n    }\n    return err\n}","preventionTips":["Authenticate before running logout-dependent flows.","Ensure the daemon/server is running for the target workspace.","Use fresh, valid workspace ids.","Give the config-fetch context a generous timeout."],"tags":["cli","network","api-client","config"],"backgroundTag":"request-transport-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}