wavetermdev/waveterm · error
error deleting subblock %s: %w
Error message
error deleting subblock %s: %w
What it means
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.
Source
Thrown at pkg/wcore/block.go:169
}
// Must delete all blocks individually first.
// Also deletes LayoutState.
// recursive: if true, will recursively close parent tab, window, workspace, if they are empty.
// Returns new active tab id, error.
func DeleteBlock(ctx context.Context, blockId string, recursive bool) error {
block, err := wstore.DBGet[*waveobj.Block](ctx, blockId)
if err != nil {
return fmt.Errorf("error getting block: %w", err)
}
if block == nil {
return nil
}
if len(block.SubBlockIds) > 0 {
for _, subBlockId := range block.SubBlockIds {
err := DeleteBlock(ctx, subBlockId, recursive)
if err != nil {
return fmt.Errorf("error deleting subblock %s: %w", subBlockId, err)
}
}
}
parentBlockCount, err := deleteBlockObj(ctx, blockId)
if err != nil {
return fmt.Errorf("error deleting block: %w", err)
}
log.Printf("DeleteBlock: parentBlockCount: %d", parentBlockCount)
parentORef := waveobj.ParseORefNoErr(block.ParentORef)
if recursive && parentORef.OType == waveobj.OType_Tab && parentBlockCount == 0 {
// if parent tab has no blocks, delete the tab
log.Printf("DeleteBlock: parent tab has no blocks, deleting tab %s", parentORef.OID)
parentWorkspaceId, err := wstore.DBFindWorkspaceForTabId(ctx, parentORef.OID)
if err != nil {
return fmt.Errorf("error finding workspace for tab to delete %s: %w", parentORef.OID, err)
}
newActiveTabId, err := DeleteTab(ctx, parentWorkspaceId, parentORef.OID, true)View on GitHub (pinned to a4447c1563)
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.
Example fix
// before
if err := wclient.DeleteBlock(ctx, blockId, false); err != nil {
// parent still has subblocks -> "block has subblocks" bubbled up
}
// after
if err := wclient.DeleteBlock(ctx, blockId, true); err != nil { // recursive=true clears subblocks first
log.Printf("subblock delete failed: %v", err)
} Defensive patterns
Strategy: retry
Validate before calling
block, _ := wstore.DBGet[*waveobj.Block](ctx, blockId)
if block != nil && len(block.SubBlockIds) > 0 {
recursive = true
} Try / catch
err := wcore.DeleteBlock(ctx, subBlockId, true)
for attempts := 0; err != nil && attempts < 3; attempts++ {
time.Sleep(50 * time.Millisecond)
err = wcore.DeleteBlock(ctx, subBlockId, true)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- error getting block: %w
- error deleting block: %w
- block has subblocks, must delete subblocks first
- error finding workspace for tab to delete %s: %w
- error deleting tab %s: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/0748460711d12a2e.
Report an issue: GitHub.