wavetermdev/waveterm · error

error setting active tab: %w

Error message

error setting active tab: %w

What it means

SetActiveTab changes the workspace's active tab and returns update events. If wcore.SetActiveTab fails (workspace or tab not found, DB error, timeout), the error is wrapped as "error setting active tab: %w". It indicates the tab switch could not be persisted before any controllers are started.

Source

Thrown at pkg/service/workspaceservice/workspaceservice.go:179

		}()
		wps.Broker.SendUpdateEvents(updates)
	}()
	return tabId, updates, nil
}

func (svc *WorkspaceService) SetActiveTab_Meta() tsgenmeta.MethodMeta {
	return tsgenmeta.MethodMeta{
		ArgNames: []string{"workspaceId", "tabId"},
	}
}

func (svc *WorkspaceService) SetActiveTab(workspaceId string, tabId string) (waveobj.UpdatesRtnType, error) {
	ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
	defer cancelFn()
	ctx = waveobj.ContextWithUpdates(ctx)
	err := wcore.SetActiveTab(ctx, workspaceId, tabId)
	if err != nil {
		return nil, fmt.Errorf("error setting active tab: %w", err)
	}
	// check all blocks in tab and start controllers (if necessary)
	tab, err := wstore.DBMustGet[*waveobj.Tab](ctx, tabId)
	if err != nil {
		return nil, fmt.Errorf("error getting tab: %w", err)
	}
	blockORefs := tab.GetBlockORefs()
	blocks, err := wstore.DBSelectORefs(ctx, blockORefs)
	if err != nil {
		return nil, fmt.Errorf("error getting tab blocks: %w", err)
	}
	updates := waveobj.ContextGetUpdatesRtn(ctx)
	go func() {
		defer func() {
			panichandler.PanicHandler("WorkspaceService:SetActiveTab:SendUpdateEvents", recover())
		}()
		wps.Broker.SendUpdateEvents(updates)
	}()

View on GitHub (pinned to a4447c1563)

Solutions

  1. Verify the tabId exists in the workspace before calling SetActiveTab
  2. Unwrap the error to see whether wcore.SetActiveTab reported a not-found vs DB failure
  3. Refresh the client's view of the workspace to clear stale tab ids
  4. Check DB health and DefaultTimeout headroom

Example fix

// before
wsSvc.SetActiveTab(workspaceId, possiblyStaleTabId)
// after
tab, err := wstore.DBGet[*waveobj.Tab](ctx, possiblyStaleTabId)
if err != nil { return fmt.Errorf("tab no longer exists: %w", err) }
wsSvc.SetActiveTab(workspaceId, tab.OID)
Defensive patterns

Strategy: validation

Validate before calling

ws, err := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId)
if err != nil { return err }
if !slices.Contains(ws.TabIds, tabId) {
    return fmt.Errorf("tab %s not in workspace %s", tabId, workspaceId)
}
_, err = svc.SetActiveTab(workspaceId, tabId)

Try / catch

_, err := svc.SetActiveTab(workspaceId, tabId)
if err != nil {
    if errors.Is(err, waveobj.ErrNotFound) { /* reload workspace state */ }
    return err
}

Prevention

When it happens

Trigger: Calling SetActiveTab(workspaceId, tabId) with a tabId that does not belong to the given workspace, a nonexistent workspaceId, or a DB/timeout failure inside wcore.SetActiveTab.

Common situations: Frontend holding a stale tabId after the tab was closed elsewhere; race between closing a tab and switching to it; corrupted workspace state.

Related errors


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