charmbracelet/crush · error
failed to list workspaces: status code %d
Error message
failed to list workspaces: status code %d
What it means
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.
Source
Thrown at internal/client/proto.go:31
"net/url"
"time"
"github.com/charmbracelet/crush/internal/config"
"github.com/charmbracelet/crush/internal/message"
"github.com/charmbracelet/crush/internal/proto"
"github.com/charmbracelet/crush/internal/pubsub"
"github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
)
// ListWorkspaces retrieves all workspaces from the server.
func (c *Client) ListWorkspaces(ctx context.Context) ([]proto.Workspace, error) {
rsp, err := c.get(ctx, "/workspaces", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list workspaces: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to list workspaces: status code %d", rsp.StatusCode)
}
var workspaces []proto.Workspace
if err := json.NewDecoder(rsp.Body).Decode(&workspaces); err != nil {
return nil, fmt.Errorf("failed to decode workspaces: %w", err)
}
return workspaces, nil
}
// CreateWorkspace creates a new workspace on the server.
func (c *Client) CreateWorkspace(ctx context.Context, ws proto.Workspace) (*proto.Workspace, error) {
ws.ClientID = c.clientID
rsp, err := c.post(ctx, "/workspaces", nil, jsonBody(ws), http.Header{"Content-Type": []string{"application/json"}})
if err != nil {
return nil, fmt.Errorf("failed to create workspace: %w", err)
}
defer rsp.Body.Close()
if err := checkStatus(rsp); err != nil {
return nil, fmt.Errorf("failed to create workspace: %w", err)View on GitHub (pinned to 7944b8e522)
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.
Example fix
// before
workspaces, err := client.ListWorkspaces(ctx)
if err != nil {
return err
}
// after
workspaces, err := client.ListWorkspaces(ctx)
if err != nil {
var statusErr interface{ HTTPStatusCode() int }
if errors.As(err, &statusErr) && statusErr.HTTPStatusCode() == http.StatusNotFound {
return restartServerWithCurrentVersion(ctx)
}
return err
} Defensive patterns
Strategy: type-guard
Validate before calling
// Verify server compatibility before listing
resp, err := http.Get(baseURL + "/workspaces")
if err == nil && resp.StatusCode == http.StatusNotFound {
return errors.New("server does not expose /workspaces; upgrade or restart the daemon")
} Type guard
func statusCodeFromListError(err error) int {
s := err.Error()
var code int
if n, _ := fmt.Sscanf(s, "failed to list workspaces: status code %d", &code); n == 1 {
return code
}
return 0
} Try / catch
workspaces, err := client.ListWorkspaces(ctx)
if err != nil {
switch statusCodeFromListError(err) {
case http.StatusNotFound:
return restartServerWithCurrentVersion(ctx)
case http.StatusServiceUnavailable:
return startFreshServer(ctx)
default:
return err
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- status code %d: %s
- status code %d
- failed to create workspace: %w
- failed to get MCP pending auth: status code %d
- failed to get MCP auth URL: status code %d
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/0f43c4c1daeba64c.
Report an issue: GitHub.