wavetermdev/waveterm · error

error getting window: %w

Error message

error getting window: %w

What it means

SwitchWorkspace loads the window with GetWindow after validating the target workspace. This error wraps a failed window lookup — the windowId does not match an existing Window object or the DB read failed. No workspace switch occurs.

Source

Thrown at pkg/wcore/window.go:29

	"github.com/google/uuid"
	"github.com/wavetermdev/waveterm/pkg/eventbus"
	"github.com/wavetermdev/waveterm/pkg/util/utilfn"
	"github.com/wavetermdev/waveterm/pkg/waveobj"
	"github.com/wavetermdev/waveterm/pkg/wshrpc"
	"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
	"github.com/wavetermdev/waveterm/pkg/wshutil"
	"github.com/wavetermdev/waveterm/pkg/wstore"
)

func SwitchWorkspace(ctx context.Context, windowId string, workspaceId string) (*waveobj.Workspace, error) {
	log.Printf("SwitchWorkspace %s %s\n", windowId, workspaceId)
	ws, err := GetWorkspace(ctx, workspaceId)
	if err != nil {
		return nil, fmt.Errorf("error getting new workspace: %w", err)
	}
	window, err := GetWindow(ctx, windowId)
	if err != nil {
		return nil, fmt.Errorf("error getting window: %w", err)
	}
	curWsId := window.WorkspaceId
	if curWsId == workspaceId {
		return nil, nil
	}

	allWindows, err := wstore.DBGetAllObjsByType[*waveobj.Window](ctx, waveobj.OType_Window)
	if err != nil {
		return nil, fmt.Errorf("error getting all windows: %w", err)
	}

	for _, w := range allWindows {
		if w.WorkspaceId == workspaceId {
			log.Printf("workspace %s already has a window %s, focusing that window\n", workspaceId, w.OID)
			client := wshclient.GetBareRpcClient()
			err = wshclient.FocusWindowCommand(client, w.OID, &wshrpc.RpcOpts{Route: wshutil.ElectronRoute})
			return nil, err
		}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Confirm the window still exists before switching (re-fetch the window list and re-check the ID)
  2. Use a valid, current window OID from wstore (OType_Window) rather than a cached value
  3. In the caller, treat not-found as benign: skip the switch or create a new window for the workspace
  4. If the wrapped error is a DB error rather than not-found, investigate database health

Example fix

// before
ws, err := wcore.SwitchWorkspace(ctx, staleWinId, wsId)
// after
win, err := wcore.GetWindow(ctx, winId)
if errors.Is(err, wstore.ErrNotFound) {
    win, err = wcore.CreateWindow(ctx, wsId) // create instead of failing
}
Defensive patterns

Strategy: validation

Validate before calling

_, err := wcore.GetWindow(ctx, windowId)
if err != nil {
    return fmt.Errorf("window %s unavailable: %w", windowId, err)
}

Type guard

func windowExists(ctx context.Context, id string) bool {
    _, err := wcore.GetWindow(ctx, id)
    return err == nil
}

Try / catch

ws, err := wcore.SwitchWorkspace(ctx, winId, wsId)
if err != nil && strings.Contains(err.Error(), "getting window") {
    // window is gone; create one for the target workspace
    ws, err = wcore.CreateWindowForWorkspace(ctx, wsId)
}

Prevention

When it happens

Trigger: Calling SwitchWorkspace(ctx, windowId, workspaceId) with a windowId that no longer exists (window closed), a malformed/non-UUID windowId, or an underlying DB read error from GetWindow.

Common situations: RPC from the frontend racing a window close; stale windowId cached in a client after the window was deleted; startup code running before windows are registered in the store.

Related errors


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