wavetermdev/waveterm · error

error finding workspace for tab to delete %s: %w

Error message

error finding workspace for tab to delete %s: %w

What it means

When recursive=true and the block's parent tab becomes empty, DeleteBlock tries to close the tab too. It first resolves the owning workspace with DBFindWorkspaceForTabId; if that lookup fails it wraps it as "error finding workspace for tab to delete %s: %w". The tab cascade cannot proceed without the workspace id needed by DeleteTab.

Source

Thrown at pkg/wcore/block.go:185

			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)
		if err != nil {
			return fmt.Errorf("error deleting tab %s: %w", parentORef.OID, err)
		}
		SendActiveTabUpdate(ctx, parentWorkspaceId, newActiveTabId)
	}
	sendBlockCloseEvent(blockId)
	return nil
}

// returns the updated block count for the parent object
func deleteBlockObj(ctx context.Context, blockId string) (int, error) {
	return wstore.WithTxRtn(ctx, func(tx *wstore.TxWrap) (int, error) {
		block, err := wstore.DBGet[*waveobj.Block](tx.Context(), blockId)
		if err != nil {
			return -1, fmt.Errorf("error getting block: %w", err)
		}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Inspect the wrapped cause; if it is a DB error, fix the store/IO issue first.
  2. Verify the tab actually belongs to a workspace (check its ParentORef); delete orphaned tabs manually.
  3. Skip the recursive cascade (recursive=false) and delete the empty tab through a separate code path once the workspace mapping is repaired.
  4. Rebuild workspace-tab relationships from block/tab ParentORef data if the index is inconsistent.

Example fix

// before
wcore.DeleteBlock(ctx, blockId, true) // fails when tab's workspace row is gone
// after
wsId, err := wstore.DBFindWorkspaceForTabId(ctx, tabId)
if err != nil {
    // repair or skip tab cascade
    return wcore.DeleteBlock(ctx, blockId, false)
}
wcore.DeleteBlock(ctx, blockId, true)
Defensive patterns

Strategy: fallback

Validate before calling

wsId, err := wstore.DBFindWorkspaceForTabId(ctx, tabId)
if err != nil {
    // fall back to non-recursive delete
    return wcore.DeleteBlock(ctx, blockId, false)
}

Try / catch

if err := wcore.DeleteBlock(ctx, blockId, true); err != nil && strings.Contains(err.Error(), "error finding workspace for tab") {
    // degrade gracefully: block deletion matters more than tab cleanup
    log.Printf("skipping tab cascade: %v", err)
}

Prevention

When it happens

Trigger: Recursive delete of the last block in a tab, but DBFindWorkspaceForTabId returns an error — the tab's workspace linkage is missing from the store or the DB query itself fails.

Common situations: Orphaned tab rows after a partial workspace deletion or corrupted store; DB read error during the workspace->tab join lookup.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/d2a7fc809898414d. Report an issue: GitHub.