{"record":{"id":"bf24aa894c723d95","repo":"wavetermdev/waveterm","slug":"workspace-already-deleted-w","errorCode":null,"errorMessage":"workspace already deleted %w","messagePattern":"workspace already deleted %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/wcore/workspace.go","lineNumber":124,"sourceCode":"\t\t\twsList = waveobj.WorkspaceList{}\n\t\t}\n\t\tws.Color = WorkspaceColors[len(wsList)%len(WorkspaceColors)]\n\t\tupdated = true\n\t}\n\tif updated {\n\t\twstore.DBUpdate(ctx, ws)\n\t}\n\treturn ws, updated, nil\n}\n\n// If force is true, it will delete even if workspace is named.\n// If workspace is empty, it will be deleted, even if it is named.\n// Returns true if workspace was deleted, false if it was not deleted.\nfunc DeleteWorkspace(ctx context.Context, workspaceId string, force bool) (bool, string, error) {\n\tlog.Printf(\"DeleteWorkspace %s\\n\", workspaceId)\n\tworkspace, err := wstore.DBMustGet[*waveobj.Workspace](ctx, workspaceId)\n\tif err != nil && wstore.ErrNotFound == err {\n\t\treturn true, \"\", fmt.Errorf(\"workspace already deleted %w\", err)\n\t}\n\t// @jalileh list needs to be saved early on i assume\n\tworkspaces, err := ListWorkspaces(ctx)\n\tif err != nil {\n\t\treturn false, \"\", fmt.Errorf(\"error retrieving workspaceList: %w\", err)\n\t}\n\n\tif workspace.Name != \"\" && workspace.Icon != \"\" && !force && len(workspace.TabIds) > 0 {\n\t\tlog.Printf(\"Ignoring DeleteWorkspace for workspace %s as it is named\\n\", workspaceId)\n\t\treturn false, \"\", nil\n\t}\n\n\tfor _, tabId := range workspace.TabIds {\n\t\tlog.Printf(\"deleting tab %s\\n\", tabId)\n\t\t_, err := DeleteTab(ctx, workspaceId, tabId, false)\n\t\tif err != nil {\n\t\t\treturn false, \"\", fmt.Errorf(\"error closing tab: %w\", err)\n\t\t}","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wcore/workspace.go#L106-L142","documentation":"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.","triggerScenarios":"Calling DeleteWorkspace, SwitchWorkspace, or CloseWindow with a workspaceId whose row was already removed (double delete, deleted by another instance).","commonSituations":"Double-clicking delete; two windows deleting the same workspace; stale id after prior cleanup.","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"],"exampleFix":"// before\nwasDeleted, _, err := DeleteWorkspace(ctx, wsId, false)\nif err != nil { return err }\n// after\nwasDeleted, _, err := DeleteWorkspace(ctx, wsId, false)\nif err != nil && strings.Contains(err.Error(), \"already deleted\") { err = nil }","handlingStrategy":"try-catch","validationCode":"_, err := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId)\nif errors.Is(err, wstore.ErrNotFound) {\n    return nil // already deleted, nothing to do\n}","typeGuard":null,"tryCatchPattern":"deleted, _, err := wcore.DeleteWorkspace(ctx, wsId, false)\nif err != nil && strings.Contains(err.Error(), \"already deleted\") {\n    return nil // idempotent success\n}\nif err != nil { return err }","preventionTips":["Treat delete as idempotent in callers","Refresh workspace lists after deletes to avoid stale ids","Serialize deletes from UI (disable button after first click)"],"tags":["go","workspace","not-found"],"backgroundTag":"record-not-found","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}