CherryHQ/cherry-studio · error · Error

Tab ${freshTabId} was created but not found - it may have be

Error message

Tab ${freshTabId} was created but not found - it may have been closed

What it means

In the newTab branch of getActiveOrCreateTab, createTab resolves with a freshTabId but windowInfo.tabs.get(freshTabId) returns undefined immediately after. Since createTab sets windowInfo.tabs.set(tabId, tabInfo) before resolving, the only way it is missing is if the view's webContents 'destroyed' handler already fired and deleted the entry — a close race right after creation.

Source

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

   * @param privateMode - Whether to use private browsing mode
   * @param tabId - Optional specific tab ID to use
   * @param newTab - If true, always create a new tab (useful for parallel requests)
   * @param showWindow - If true, shows the browser window (default: false)
   */
  private async getTab(
    privateMode: boolean,
    tabId?: string,
    newTab?: boolean,
    showWindow = false
  ): Promise<{ tabId: string; tab: TabInfo }> {
    const windowInfo = await this.getOrCreateWindow(privateMode, showWindow)

    // If newTab is requested, create a fresh tab
    if (newTab) {
      const { tabId: freshTabId } = await this.createTab(privateMode, showWindow)
      const tab = windowInfo.tabs.get(freshTabId)
      if (!tab) {
        throw new Error(`Tab ${freshTabId} was created but not found - it may have been closed`)
      }
      return { tabId: freshTabId, tab }
    }

    if (tabId) {
      const tab = windowInfo.tabs.get(tabId)
      if (tab && !tab.view.webContents.isDestroyed()) {
        this.touchTab(windowInfo.windowKey, tabId)
        return { tabId, tab }
      }
    }

    // Use active tab or create new one
    if (windowInfo.activeTabId) {
      const activeTab = windowInfo.tabs.get(windowInfo.activeTabId)
      if (activeTab && !activeTab.view.webContents.isDestroyed()) {
        this.touchTab(windowInfo.windowKey, windowInfo.activeTabId)
        return { tabId: windowInfo.activeTabId, tab: activeTab }

View on GitHub (pinned to 726446b54c)

Solutions

  1. Treat the missing tab as 'creation lost' and retry once via createTab, or surface a clear 'browser tab unavailable' error to the agent.
  2. Investigate why the webContents was destroyed instantly — check Electron GPU/sandbox logs and ensure the app is not shutting down.
  3. Avoid invoking browser tools during teardown; gate with ensureAppReady and an isDisposing flag.
  4. Serialize createTab calls per window to reduce destruction races.

Example fix

// before
const { tabId: freshTabId } = await this.createTab(privateMode, showWindow)
const tab = windowInfo.tabs.get(freshTabId)
if (!tab) {
  throw new Error(`Tab ${freshTabId} was created but not found - it may have been closed`)
}

// after — retry once before giving up; detect teardown
let tab = windowInfo.tabs.get(freshTabId)
if (!tab) {
  if (this.disposing) throw new Error('Browser controller is shutting down')
  const retry = await this.createTab(privateMode, showWindow)
  tab = windowInfo.tabs.get(retry.tabId)
}
if (!tab) throw new Error('Tab creation failed; webContents was destroyed immediately')
return { tabId: freshTabId, tab }
Defensive patterns

Strategy: try-catch

Validate before calling

// Retry once when a freshly created tab is missing due to a destruction race
async function createTabStable(controller, windowInfo, privateMode, showWindow) {
  const { tabId } = await controller.createTab(privateMode, showWindow)
  let tab = windowInfo.tabs.get(tabId)
  if (!tab && !controller.disposing) {
    const retry = await controller.createTab(privateMode, showWindow)
    tab = windowInfo.tabs.get(retry.tabId)
  }
  if (!tab) throw new Error('tab creation failed; webContents destroyed immediately')
  return tab
}

Try / catch

try {
  return await createTabStable(controller, windowInfo, privateMode, showWindow)
} catch (e) {
  if (controller.disposing) throw new Error('browser controller shutting down')
  throw e
}

Prevention

When it happens

Trigger: A newly created BrowserView's webContents is destroyed synchronously or in the same tick after createTab resolves (GPU crash, forced sandbox teardown, app shutdown), triggering the 'destroyed' listener (controller.ts:480) which calls windowInfo.tabs.delete(tabId) before the caller reads it.

Common situations: Browser tool invoked during app quit/suspend; Electron sandbox or GPU process crash on view creation; a concurrent reset()/dispose() tearing down the window right as a new tab is spawned; resource exhaustion destroying the new view instantly.

Related errors


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