charmbracelet/crush · error
failed to delete workspace: %w
Error message
failed to delete workspace: %w
What it means
DeleteWorkspace wraps an error from the underlying c.delete transport call for DELETE /workspaces/{id}?client_id=... . This is raised before status checking, so it reflects a transport-level failure: the request never completed successfully at the HTTP layer. The wrapped cause (connection error, canceled context, etc.) is available via errors.Is/As.
Source
Thrown at internal/client/proto.go:80
return nil, fmt.Errorf("failed to get workspace: %w", err)
}
defer rsp.Body.Close()
if err := checkStatus(rsp); err != nil {
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),View on GitHub (pinned to 7944b8e522)
Solutions
- Check the server is running and reachable before deleting.
- Retry the delete with backoff; deletes are idempotent enough to tolerate re-issue (a 404 on retry means it succeeded).
- Confirm the server URL/port in the client configuration.
- Inspect errors.Is(err, context.Canceled) to avoid retrying caller-initiated cancels.
Example fix
// before
if err := client.DeleteWorkspace(ctx, id); err != nil { return err }
// after: retry transient transport errors
err := client.DeleteWorkspace(ctx, id)
for i := 0; err != nil && i < 3; i++ {
if errors.Is(err, context.Canceled) { return err }
time.Sleep(time.Duration(1<<i) * 100 * time.Millisecond)
err = client.DeleteWorkspace(ctx, id)
} Defensive patterns
Strategy: retry
Validate before calling
if !serverReachable(serverURL) { // e.g. net.DialTimeout probe
return fmt.Errorf("skip delete: server unreachable")
} Try / catch
err := client.DeleteWorkspace(ctx, id)
if err != nil {
if errors.Is(err, context.Canceled) { return err }
// transport error: retry once; a 404 on retry means success
if rerr := client.DeleteWorkspace(ctx, id); rerr != nil && !strings.Contains(rerr.Error(), "404") {
return rerr
}
} Prevention
- Keep the server process supervised (auto-restart).
- Use retries with exponential backoff for deletes.
- Treat 404 as idempotent success.
- Avoid holding short contexts over delete operations.
When it happens
Trigger: Calling client.DeleteWorkspace(ctx, id) when the server is down or unreachable, the connection is reset mid-request, or the context is canceled before the DELETE completes.
Common situations: crush server stopped between listing and deleting a workspace; network blip on a laptop switching networks; context timeout too short for the delete; wrong server address after config change.
Related errors
- failed to get workspace: %w
- failed to make request: %w
- failed to refresh OAuth token: %w
- failed to check project init: %w
- failed to mark project initialized: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/4ac709bc4b80f3c6.
Report an issue: GitHub.