{"record":{"id":"0748460711d12a2e","repo":"wavetermdev/waveterm","slug":"error-deleting-subblock-s-w","errorCode":null,"errorMessage":"error deleting subblock %s: %w","messagePattern":"error deleting subblock (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wcore/block.go","lineNumber":169,"sourceCode":"}\n\n// Must delete all blocks individually first.\n// Also deletes LayoutState.\n// recursive: if true, will recursively close parent tab, window, workspace, if they are empty.\n// Returns new active tab id, error.\nfunc DeleteBlock(ctx context.Context, blockId string, recursive bool) error {\n\tblock, err := wstore.DBGet[*waveobj.Block](ctx, blockId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error getting block: %w\", err)\n\t}\n\tif block == nil {\n\t\treturn nil\n\t}\n\tif len(block.SubBlockIds) > 0 {\n\t\tfor _, subBlockId := range block.SubBlockIds {\n\t\t\terr := DeleteBlock(ctx, subBlockId, recursive)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error deleting subblock %s: %w\", subBlockId, err)\n\t\t\t}\n\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)","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wcore/block.go#L151-L187","documentation":"DeleteBlock recursively deletes a block's SubBlockIds before deleting the block itself. If any recursive DeleteBlock call on a subblock fails, the error is wrapped with the subblock id as \"error deleting subblock %s: %w\". The whole delete aborts so no orphaned subblocks are left behind.","triggerScenarios":"Any failure that DeleteBlock can return, triggered while deleting a child subblock: DB read failure, deleteBlockObj failure (e.g. the subblock itself still has subblocks), or recursive tab cleanup failure.","commonSituations":"Deeply nested block trees where an inner delete fails; a subblock id referencing a block that still has its own subblocks due to a prior partial/interrupted delete.","solutions":["Unwrap the chain to find the innermost cause and failing subblock id.","Delete the offending subblock's own subblocks first (or call DeleteBlock again — it recurses).","Retry the whole delete; the operation is safe to repeat for already-deleted ids.","If a corrupted tree keeps failing, manually remove subblock ids from the parent block's SubBlockIds via the store."],"exampleFix":"// before\nif err := wclient.DeleteBlock(ctx, blockId, false); err != nil {\n    // parent still has subblocks -> \"block has subblocks\" bubbled up\n}\n// after\nif err := wclient.DeleteBlock(ctx, blockId, true); err != nil { // recursive=true clears subblocks first\n    log.Printf(\"subblock delete failed: %v\", err)\n}","handlingStrategy":"retry","validationCode":"block, _ := wstore.DBGet[*waveobj.Block](ctx, blockId)\nif block != nil && len(block.SubBlockIds) > 0 {\n    recursive = true\n}","typeGuard":null,"tryCatchPattern":"err := wcore.DeleteBlock(ctx, subBlockId, true)\nfor attempts := 0; err != nil && attempts < 3; attempts++ {\n    time.Sleep(50 * time.Millisecond)\n    err = wcore.DeleteBlock(ctx, subBlockId, true)\n}","preventionTips":["Always use recursive=true when deleting blocks that may contain subblocks.","Log the failing subblock id from the wrapped message before retrying.","Avoid concurrent deletes on the same block tree."],"tags":["wave-terminal","block-deletion","subblocks","recursion","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"}