wavetermdev/waveterm · warning
workspace already deleted %w
Error message
workspace already deleted %w
What it means
DeleteWorkspace detected the workspace record is already absent (wstore.ErrNotFound) yet still returns an error — a quirk: the code returns (true, "", err) meaning it was already deleted but with a descriptive error attached. The wrapped cause is the DB ErrNotFound.
Source
Thrown at pkg/wcore/workspace.go:124
wsList = waveobj.WorkspaceList{}
}
ws.Color = WorkspaceColors[len(wsList)%len(WorkspaceColors)]
updated = true
}
if updated {
wstore.DBUpdate(ctx, ws)
}
return ws, updated, nil
}
// If force is true, it will delete even if workspace is named.
// If workspace is empty, it will be deleted, even if it is named.
// Returns true if workspace was deleted, false if it was not deleted.
func DeleteWorkspace(ctx context.Context, workspaceId string, force bool) (bool, string, error) {
log.Printf("DeleteWorkspace %s\n", workspaceId)
workspace, err := wstore.DBMustGet[*waveobj.Workspace](ctx, workspaceId)
if err != nil && wstore.ErrNotFound == err {
return true, "", fmt.Errorf("workspace already deleted %w", err)
}
// @jalileh list needs to be saved early on i assume
workspaces, err := ListWorkspaces(ctx)
if err != nil {
return false, "", fmt.Errorf("error retrieving workspaceList: %w", err)
}
if workspace.Name != "" && workspace.Icon != "" && !force && len(workspace.TabIds) > 0 {
log.Printf("Ignoring DeleteWorkspace for workspace %s as it is named\n", workspaceId)
return false, "", nil
}
for _, tabId := range workspace.TabIds {
log.Printf("deleting tab %s\n", tabId)
_, err := DeleteTab(ctx, workspaceId, tabId, false)
if err != nil {
return false, "", fmt.Errorf("error closing tab: %w", err)
}View on GitHub (pinned to a4447c1563)
Solutions
- Treat ErrNotFound-wrapped as already-deleted and ignore, or check existence with DBGet first before deleting
- Refresh the workspace list in the UI to drop the stale entry
- Guard DeleteWorkspace to skip ids no longer present
Example fix
// before
wasDeleted, _, err := DeleteWorkspace(ctx, wsId, false)
if err != nil { return err }
// after
wasDeleted, _, err := DeleteWorkspace(ctx, wsId, false)
if err != nil && strings.Contains(err.Error(), "already deleted") { err = nil } Defensive patterns
Strategy: try-catch
Validate before calling
_, err := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId)
if errors.Is(err, wstore.ErrNotFound) {
return nil // already deleted, nothing to do
} Try / catch
deleted, _, err := wcore.DeleteWorkspace(ctx, wsId, false)
if err != nil && strings.Contains(err.Error(), "already deleted") {
return nil // idempotent success
}
if err != nil { return err } Prevention
- Treat delete as idempotent in callers
- Refresh workspace lists after deletes to avoid stale ids
- Serialize deletes from UI (disable button after first click)
When it happens
Trigger: Calling DeleteWorkspace, SwitchWorkspace, or CloseWindow with a workspaceId whose row was already removed (double delete, deleted by another instance).
Common situations: Double-clicking delete; two windows deleting the same workspace; stale id after prior cleanup.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- workspace %s not found: %w
- %s: no such file
- chat not found: %s
- tool call with ID %s not found in chat %s
- error updating workspace: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/bf24aa894c723d95.
Report an issue: GitHub.