{"record":{"id":"7ea6060462153504","repo":"wavetermdev/waveterm","slug":"error-deleting-tab-s-w","errorCode":null,"errorMessage":"error deleting tab %s: %w","messagePattern":"error deleting tab (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wcore/block.go","lineNumber":189,"sourceCode":"\t\t}\n\t}\n\tparentBlockCount, err := deleteBlockObj(ctx, blockId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error deleting block: %w\", err)\n\t}\n\tlog.Printf(\"DeleteBlock: parentBlockCount: %d\", parentBlockCount)\n\tparentORef := waveobj.ParseORefNoErr(block.ParentORef)\n\n\tif recursive && parentORef.OType == waveobj.OType_Tab && parentBlockCount == 0 {\n\t\t// if parent tab has no blocks, delete the tab\n\t\tlog.Printf(\"DeleteBlock: parent tab has no blocks, deleting tab %s\", parentORef.OID)\n\t\tparentWorkspaceId, err := wstore.DBFindWorkspaceForTabId(ctx, parentORef.OID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error finding workspace for tab to delete %s: %w\", parentORef.OID, err)\n\t\t}\n\t\tnewActiveTabId, err := DeleteTab(ctx, parentWorkspaceId, parentORef.OID, true)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error deleting tab %s: %w\", parentORef.OID, err)\n\t\t}\n\t\tSendActiveTabUpdate(ctx, parentWorkspaceId, newActiveTabId)\n\t}\n\tsendBlockCloseEvent(blockId)\n\treturn nil\n}\n\n// returns the updated block count for the parent object\nfunc deleteBlockObj(ctx context.Context, blockId string) (int, error) {\n\treturn wstore.WithTxRtn(ctx, func(tx *wstore.TxWrap) (int, error) {\n\t\tblock, err := wstore.DBGet[*waveobj.Block](tx.Context(), blockId)\n\t\tif err != nil {\n\t\t\treturn -1, fmt.Errorf(\"error getting block: %w\", err)\n\t\t}\n\t\tif block == nil {\n\t\t\treturn -1, fmt.Errorf(\"block not found: %q\", blockId)\n\t\t}\n\t\tif len(block.SubBlockIds) > 0 {","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wcore/block.go#L171-L207","documentation":"In the recursive path, after finding the workspace DeleteBlock calls DeleteTab to close the now-empty parent tab. Any error from DeleteTab is wrapped as \"error deleting tab %s: %w\" with the tab id. The block deletion itself already succeeded; only the tab cascade failed.","triggerScenarios":"DeleteTab fails during recursive block deletion of the last block in a tab — e.g. tab lookup failure, tab still has other blocks, or the tab delete transaction fails.","commonSituations":"Race where another client added a block to the tab between the count check and DeleteTab; store/transaction failure; invalid workspace id passed into DeleteTab.","solutions":["Read the wrapped DeleteTab cause for the specific failure.","Re-check the tab's block count; if it is no longer empty, the cascade is unnecessary and can be skipped.","Retry after resolving the store issue; the block is already deleted, only the tab remains.","Handle the failure gracefully — sendBlockCloseEvent is skipped, so emit block-close UI updates manually."],"exampleFix":"// before\nerr := wcore.DeleteBlock(ctx, blockId, true) // tab cascade failure aborts\n// after\nif err := wcore.DeleteBlock(ctx, blockId, true); err != nil && strings.Contains(err.Error(), \"error deleting tab\") {\n    // block was deleted; clean up the empty tab separately\n    wcore.DeleteTab(ctx, wsId, tabId, false)\n}","handlingStrategy":"try-catch","validationCode":"tab, _ := wstore.DBGet[*waveobj.Tab](ctx, tabId)\nblocks, _ := wstore.DBGetBlocksForTab(ctx, tabId)\ncascade := tab != nil && len(blocks) == 1 // only this block remains","typeGuard":null,"tryCatchPattern":"err := wcore.DeleteBlock(ctx, blockId, true)\nif err != nil && strings.Contains(err.Error(), \"error deleting tab\") {\n    // block already deleted; handle leftover tab separately\n    log.Printf(\"tab cascade failed: %v\", err)\n    return nil\n}","preventionTips":["Treat block deletion and tab cascade as separable outcomes when handling errors.","Avoid concurrent edits to the tab while a recursive delete runs.","After a cascade failure, re-check whether the tab still exists before retrying."],"tags":["wave-terminal","tab-deletion","cascade-delete","wrapped-error"],"backgroundTag":"cascade-delete-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}