{"record":{"id":"3a070d7f5f71dbac","repo":"charmbracelet/crush","slug":"failed-to-list-workspaces-w","errorCode":null,"errorMessage":"failed to list workspaces: %w","messagePattern":"failed to list workspaces: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":27,"sourceCode":"\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/charmbracelet/crush/internal/config\"\n\t\"github.com/charmbracelet/crush/internal/message\"\n\t\"github.com/charmbracelet/crush/internal/proto\"\n\t\"github.com/charmbracelet/crush/internal/pubsub\"\n\t\"github.com/charmbracelet/x/powernap/pkg/lsp/protocol\"\n)\n\n// ListWorkspaces retrieves all workspaces from the server.\nfunc (c *Client) ListWorkspaces(ctx context.Context) ([]proto.Workspace, error) {\n\trsp, err := c.get(ctx, \"/workspaces\", nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to list workspaces: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"failed to list workspaces: status code %d\", rsp.StatusCode)\n\t}\n\tvar workspaces []proto.Workspace\n\tif err := json.NewDecoder(rsp.Body).Decode(&workspaces); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode workspaces: %w\", err)\n\t}\n\treturn workspaces, nil\n}\n\n// CreateWorkspace creates a new workspace on the server.\nfunc (c *Client) CreateWorkspace(ctx context.Context, ws proto.Workspace) (*proto.Workspace, error) {\n\tws.ClientID = c.clientID\n\trsp, err := c.post(ctx, \"/workspaces\", nil, jsonBody(ws), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create workspace: %w\", err)","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L9-L45","documentation":"ListWorkspaces calls GET /workspaces; this error wraps any transport-level failure from the HTTP client (connection refused, timeout, canceled context) before a response is obtained. It is a request-sending failure, not a server-reported status.","triggerScenarios":"Calling ListWorkspaces when the daemon is not running or unreachable, the context is canceled, the connection is refused/reset, or DNS/TCP fails for the configured server address.","commonSituations":"The background server died or was never started; wrong port/address in configuration; firewall blocking localhost port; context deadline exceeded under slow startup; shutdownLegacyStaleServer probing a server that already exited.","solutions":["Check the wrapped error: connection refused means the daemon is not running — start it.","Verify the server address/port configured for the client.","Check for context cancellation/deadline and increase the timeout if the server is slow to start.","Ensure no firewall or proxy blocks the connection to the daemon."],"exampleFix":"// before\nworkspaces, err := client.ListWorkspaces(ctx)\nif err != nil {\n\treturn err\n}\n// after\nworkspaces, err := client.ListWorkspaces(ctx)\nif err != nil {\n\tif isConnectionRefused(err) {\n\t\tif startErr := startServer(ctx); startErr != nil {\n\t\t\treturn startErr\n\t\t}\n\t\tworkspaces, err = client.ListWorkspaces(ctx)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n}","handlingStrategy":"retry","validationCode":"// Verify the daemon is listening before calling\nconn, err := net.DialTimeout(\"tcp\", serverAddr, 2*time.Second)\nif err != nil {\n\treturn fmt.Errorf(\"daemon not reachable at %s: %w\", serverAddr, err)\n}\nconn.Close()","typeGuard":"func isTransportError(err error) bool {\n\t// Wrapped transport failure: no \"status code\" component means the request never completed\n\ts := err.Error()\n\treturn !strings.Contains(s, \"status code\")\n}","tryCatchPattern":"workspaces, err := client.ListWorkspaces(ctx)\nif err != nil {\n\tvar netErr net.Error\n\tif errors.As(err, &netErr) || strings.Contains(err.Error(), \"connection refused\") {\n\t\tif startErr := startDaemon(ctx); startErr == nil {\n\t\t\tworkspaces, err = client.ListWorkspaces(ctx)\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n}","preventionTips":["Start/verify the daemon before client initialization.","Use a generous context timeout during startup to absorb slow server boot.","Confirm the configured address/port matches the running daemon.","Retry transport errors with exponential backoff before failing."],"tags":["network","http","client","connection"],"backgroundTag":"connection-refused","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}