wavetermdev/waveterm · error
error closing tab: %w
Error message
error closing tab: %w
What it means
CloseTab deletes the tab via wcore.DeleteTab(ctx, workspaceId, tabId, true) and computes the next active tab. If deletion fails, the error is wrapped as "error closing tab: %w" and CloseTabRtn is left nil. The wrapping preserves the wcore-level cause (not found, DB error, etc.).
Source
Thrown at pkg/service/workspaceservice/workspaceservice.go:230
ArgNames: []string{"ctx", "workspaceId", "tabId", "fromElectron"},
ReturnDesc: "CloseTabRtn",
}
}
// returns the new active tabid
func (svc *WorkspaceService) CloseTab(ctx context.Context, workspaceId string, tabId string, fromElectron bool) (*CloseTabRtnType, waveobj.UpdatesRtnType, error) {
ctx = waveobj.ContextWithUpdates(ctx)
tab, err := wstore.DBGet[*waveobj.Tab](ctx, tabId)
if err == nil && tab != nil {
go func() {
for _, blockId := range tab.BlockIds {
blockcontroller.DestroyBlockController(blockId)
}
}()
}
newActiveTabId, err := wcore.DeleteTab(ctx, workspaceId, tabId, true)
if err != nil {
return nil, nil, fmt.Errorf("error closing tab: %w", err)
}
rtn := &CloseTabRtnType{}
if newActiveTabId == "" {
rtn.CloseWindow = true
} else {
rtn.NewActiveTabId = newActiveTabId
}
updates := waveobj.ContextGetUpdatesRtn(ctx)
go func() {
defer func() {
panichandler.PanicHandler("WorkspaceService:CloseTab:SendUpdateEvents", recover())
}()
wps.Broker.SendUpdateEvents(updates)
}()
return rtn, updates, nil
}
View on GitHub (pinned to a4447c1563)
Solutions
- Confirm the tabId still exists in the workspace before calling CloseTab
- Unwrap the error to see wcore.DeleteTab's root cause
- Refresh the workspace view in the client to drop stale tab references
- Check DB health/writability
Example fix
// before
wsSvc.CloseTab(wsId, tabId) // may fail if already closed
// after
tab, err := wstore.DBGet[*waveobj.Tab](ctx, tabId)
if err != nil { return nil } // tab already gone
wsSvc.CloseTab(wsId, tabId) Defensive patterns
Strategy: validation
Validate before calling
tab, err := wstore.DBGet[*waveobj.Tab](ctx, tabId)
if err != nil {
return nil // already closed; nothing to do
}
_, _, err = svc.CloseTab(workspaceId, tabId) Try / catch
_, _, err := svc.CloseTab(workspaceId, tabId)
if err != nil {
if errors.Is(errors.Unwrap(err), waveobj.ErrNotFound) { return nil } // treat as success
return err
} Prevention
- Treat not-found on close as idempotent success in callers
- Debounce duplicate close requests from the UI
- Keep workspace state synced across windows
When it happens
Trigger: Calling CloseTab(workspaceId, tabId) with a tabId not present in the workspace, a nonexistent workspaceId, or a wcore.DeleteTab storage failure.
Common situations: User closing a tab that was already closed in another window; stale frontend state; DB write failure during delete.
Related errors
- error creating tab: %w
- error setting active tab: %w
- error getting tab: %w
- error getting tab: %w
- error getting tab blocks: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/f6c065388d717693.
Report an issue: GitHub.