{"record":{"id":"0f43c4c1daeba64c","repo":"charmbracelet/crush","slug":"failed-to-list-workspaces-status-code-d","errorCode":null,"errorMessage":"failed to list workspaces: status code %d","messagePattern":"failed to list workspaces: status code (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":31,"sourceCode":"\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)\n\t}\n\tdefer rsp.Body.Close()\n\tif err := checkStatus(rsp); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create workspace: %w\", err)","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L13-L49","documentation":"ListWorkspaces expects HTTP 200 from GET /workspaces; any other status yields this error carrying the numeric code. Unlike checkStatus-based endpoints, this path bypasses sentinel wrapping, so callers only get the status number.","triggerScenarios":"Calling ListWorkspaces against a server that responds non-200 — e.g. 404 because the server version lacks /workspaces, 503 while shutting down, or 401 when auth is required.","commonSituations":"A legacy/stale server from an older version running on the port (missing endpoint); server mid-shutdown; proxy intercepting with a non-200 challenge page; version mismatch between client and daemon.","solutions":["Check the status code: 404 implies an old/incompatible server — restart it with the current version.","503 implies the server is shutting down — start a fresh daemon.","Verify the server address targets the intended daemon, not a stale process.","Compare client and server versions to ensure the /workspaces endpoint exists."],"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\tvar statusErr interface{ HTTPStatusCode() int }\n\tif errors.As(err, &statusErr) && statusErr.HTTPStatusCode() == http.StatusNotFound {\n\t\treturn restartServerWithCurrentVersion(ctx)\n\t}\n\treturn err\n}","handlingStrategy":"type-guard","validationCode":"// Verify server compatibility before listing\nresp, err := http.Get(baseURL + \"/workspaces\")\nif err == nil && resp.StatusCode == http.StatusNotFound {\n\treturn errors.New(\"server does not expose /workspaces; upgrade or restart the daemon\")\n}","typeGuard":"func statusCodeFromListError(err error) int {\n\ts := err.Error()\n\tvar code int\n\tif n, _ := fmt.Sscanf(s, \"failed to list workspaces: status code %d\", &code); n == 1 {\n\t\treturn code\n\t}\n\treturn 0\n}","tryCatchPattern":"workspaces, err := client.ListWorkspaces(ctx)\nif err != nil {\n\tswitch statusCodeFromListError(err) {\n\tcase http.StatusNotFound:\n\t\treturn restartServerWithCurrentVersion(ctx)\n\tcase http.StatusServiceUnavailable:\n\t\treturn startFreshServer(ctx)\n\tdefault:\n\t\treturn err\n\t}\n}","preventionTips":["Keep daemon and client on the same version so /workspaces exists.","Kill stale daemons from older versions before connecting.","Handle non-200 statuses explicitly since this path does not wrap sentinels.","Include the status code in logs to distinguish route-missing vs. shutdown."],"tags":["http","status-code","client","workspaces"],"backgroundTag":"http-error-response","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}