charmbracelet/crush · error

failed to delete workspace: status code %d

Error message

failed to delete workspace: status code %d

What it means

DeleteWorkspace requires an exact HTTP 200; any other status (e.g. 403, 404, 409, 500) produces this error with the status code embedded. Unlike checkStatus-based paths this branch reports only the numeric code, so the server's error detail body is not included. It means the server actively rejected or failed the delete.

Source

Thrown at internal/client/proto.go:84

		return nil, fmt.Errorf("failed to get workspace: %w", err)
	}
	var ws proto.Workspace
	if err := json.NewDecoder(rsp.Body).Decode(&ws); err != nil {
		return nil, fmt.Errorf("failed to decode workspace: %w", err)
	}
	return &ws, nil
}

// DeleteWorkspace deletes a workspace on the server.
func (c *Client) DeleteWorkspace(ctx context.Context, id string) error {
	q := url.Values{"client_id": []string{c.clientID}}
	rsp, err := c.delete(ctx, fmt.Sprintf("/workspaces/%s", id), q, nil)
	if err != nil {
		return fmt.Errorf("failed to delete workspace: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to delete workspace: status code %d", rsp.StatusCode)
	}
	return nil
}

// SetCurrentSession reports the client's current-session selection
// for the named workspace. An empty sessionID clears the entry. The
// request carries the process-scoped client ID minted in [NewClient]
// as a query parameter so the server can route the update to the
// correct [clientState] entry.
func (c *Client) SetCurrentSession(ctx context.Context, workspaceID, sessionID string) error {
	q := url.Values{"client_id": []string{c.clientID}}
	rsp, err := c.post(
		ctx,
		fmt.Sprintf("/workspaces/%s/current-session", workspaceID),
		q,
		jsonBody(proto.CurrentSession{SessionID: sessionID}),
		http.Header{"Content-Type": []string{"application/json"}},
	)

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Treat 404 as success (workspace already gone) and ignore it.
  2. Re-list workspaces (ListWorkspaces) to refresh ids before deleting.
  3. If ownership errors occur, create the workspace with the current client (CreateWorkspace stamps ws.ClientID) or restart from a fresh list.
  4. Check server logs for the status code to find the 5xx cause.

Example fix

// before
if err := client.DeleteWorkspace(ctx, id); err != nil { return err }
// after: tolerate already-deleted
if err := client.DeleteWorkspace(ctx, id); err != nil {
    if strings.Contains(err.Error(), "status code 404") {
        return nil
    }
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

// verify existence and ownership before deleting
wss, err := client.ListWorkspaces(ctx)
if err != nil { return err }
owned := false
for _, w := range wss {
    if w.ID == id && w.ClientID == myClientID { owned = true }
}
if !owned { return fmt.Errorf("workspace %q not owned by this client", id) }

Try / catch

err := client.DeleteWorkspace(ctx, id)
var statusErr interface{ StatusCode() int }
switch {
case err == nil:
    return nil
case strings.Contains(err.Error(), "status code 404"):
    return nil // already gone
default:
    return err
}

Prevention

When it happens

Trigger: Calling client.DeleteWorkspace(ctx, id) when the workspace does not exist (404), the client_id does not own the workspace (403/409), or the server errors while removing state (5xx).

Common situations: Deleting an already-deleted workspace from another client session; ownership mismatch because the client was restarted and minted a new client ID (NewClient generates a process-scoped ID); stale workspace list from before a server restart.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/cde074c65014eeef. Report an issue: GitHub.