CherryHQ/cherry-studio · error · Error

Window not found for ${privateMode ? 'private' : 'normal'} m

Error message

Window not found for ${privateMode ? 'private' : 'normal'} mode

What it means

switchTab computes a windowKey from privateMode and looks up this.windows.get(windowKey); if no window was ever created for that mode, it throws. switchTab assumes the window already exists — it does not auto-create.

Source

Thrown at src/main/ai/mcp/servers/browser/controller.ts:928

  /**
   * Closes a specific tab
   * @param privateMode - If true, closes tab from private window (default: false)
   * @param tabId - Tab identifier to close
   */
  public async closeTab(privateMode: boolean, tabId: string) {
    await this.reset(privateMode, tabId)
  }

  /**
   * Switches the active tab
   * @param privateMode - If true, switches tab in private window (default: false)
   * @param tabId - Tab identifier to switch to
   */
  public async switchTab(privateMode: boolean, tabId: string) {
    const windowKey = this.getWindowKey(privateMode)
    const windowInfo = this.windows.get(windowKey)
    if (!windowInfo) throw new Error(`Window not found for ${privateMode ? 'private' : 'normal'} mode`)

    const tab = windowInfo.tabs.get(tabId)
    if (!tab) throw new Error(`Tab ${tabId} not found`)

    // Remove previous active tab view (but NOT the tabBarView)
    if (windowInfo.activeTabId && windowInfo.activeTabId !== tabId) {
      const prevTab = windowInfo.tabs.get(windowInfo.activeTabId)
      if (prevTab && !windowInfo.window.isDestroyed()) {
        windowInfo.window.removeBrowserView(prevTab.view)
      }
    }

    windowInfo.activeTabId = tabId

    // Add the new active tab view
    if (!windowInfo.window.isDestroyed()) {
      windowInfo.window.addBrowserView(tab.view)
      this.updateViewBounds(windowInfo)

View on GitHub (pinned to 726446b54c)

Solutions

  1. Ensure a window exists for the mode first — call navigate_to or create_tab (which go through getOrCreateWindow) before switchTab.
  2. If exposing switchTab to agents, have it auto-create the window via getOrCreateWindow when absent, or return a clear 'no window open' result instead of throwing.
  3. Verify the privateMode flag matches the window the tabId came from.

Example fix

// before
const windowInfo = this.windows.get(windowKey)
if (!windowInfo) throw new Error(`Window not found for ${privateMode ? 'private' : 'normal'} mode`)

// after — bootstrap lazily or return a structured 'no window' result
let windowInfo = this.windows.get(windowKey)
if (!windowInfo) {
  await this.getOrCreateWindow(privateMode, false)
  windowInfo = this.windows.get(windowKey)
}
if (!windowInfo) throw new Error(`Unable to establish ${privateMode ? 'private' : 'normal'} browser window`)
Defensive patterns

Strategy: validation

Validate before calling

// Ensure a window exists for the mode before switching tabs
async function ensureWindowForMode(controller, privateMode) {
  const windowKey = controller.getWindowKey(privateMode)
  if (!controller.windows.get(windowKey)) {
    await controller.getOrCreateWindow(privateMode, false)
  }
}

Try / catch

try {
  await ensureWindowForMode(controller, privateMode)
  return controller.switchTab(privateMode, tabId)
} catch (e) {
  return { content: [{ type: 'text', text: `No ${privateMode ? 'private' : 'normal'} browser window open` }], isError: true }
}

Prevention

When it happens

Trigger: Calling switchTab(privateMode, tabId) before any window/tab exists for that mode — e.g. the agent calls switch_tab directly without first issuing a navigate/create_tab that would bootstrap the window via getOrCreateWindow.

Common situations: Agent invokes tools out of order (switch before open); the window for that mode was closed (the 'closed' handler deletes the entry) and the agent still holds a stale tabId; private vs. normal mode mismatch between calls.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/0688149559b3fc90. Report an issue: GitHub.