{"record":{"id":"f5e391453212e102","repo":"CherryHQ/cherry-studio","slug":"tab-freshtabid-was-created-but-not-found-it-m","errorCode":null,"errorMessage":"Tab ${freshTabId} was created but not found - it may have been closed","messagePattern":"Tab (.+?) was created but not found - it may have been closed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/servers/browser/controller.ts","lineNumber":570,"sourceCode":"   * @param privateMode - Whether to use private browsing mode\n   * @param tabId - Optional specific tab ID to use\n   * @param newTab - If true, always create a new tab (useful for parallel requests)\n   * @param showWindow - If true, shows the browser window (default: false)\n   */\n  private async getTab(\n    privateMode: boolean,\n    tabId?: string,\n    newTab?: boolean,\n    showWindow = false\n  ): Promise<{ tabId: string; tab: TabInfo }> {\n    const windowInfo = await this.getOrCreateWindow(privateMode, showWindow)\n\n    // If newTab is requested, create a fresh tab\n    if (newTab) {\n      const { tabId: freshTabId } = await this.createTab(privateMode, showWindow)\n      const tab = windowInfo.tabs.get(freshTabId)\n      if (!tab) {\n        throw new Error(`Tab ${freshTabId} was created but not found - it may have been closed`)\n      }\n      return { tabId: freshTabId, tab }\n    }\n\n    if (tabId) {\n      const tab = windowInfo.tabs.get(tabId)\n      if (tab && !tab.view.webContents.isDestroyed()) {\n        this.touchTab(windowInfo.windowKey, tabId)\n        return { tabId, tab }\n      }\n    }\n\n    // Use active tab or create new one\n    if (windowInfo.activeTabId) {\n      const activeTab = windowInfo.tabs.get(windowInfo.activeTabId)\n      if (activeTab && !activeTab.view.webContents.isDestroyed()) {\n        this.touchTab(windowInfo.windowKey, windowInfo.activeTabId)\n        return { tabId: windowInfo.activeTabId, tab: activeTab }","sourceCodeStart":552,"sourceCodeEnd":588,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/servers/browser/controller.ts#L552-L588","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Treat the missing tab as 'creation lost' and retry once via createTab, or surface a clear 'browser tab unavailable' error to the agent.","Investigate why the webContents was destroyed instantly — check Electron GPU/sandbox logs and ensure the app is not shutting down.","Avoid invoking browser tools during teardown; gate with ensureAppReady and an isDisposing flag.","Serialize createTab calls per window to reduce destruction races."],"exampleFix":"// before\nconst { tabId: freshTabId } = await this.createTab(privateMode, showWindow)\nconst tab = windowInfo.tabs.get(freshTabId)\nif (!tab) {\n  throw new Error(`Tab ${freshTabId} was created but not found - it may have been closed`)\n}\n\n// after — retry once before giving up; detect teardown\nlet tab = windowInfo.tabs.get(freshTabId)\nif (!tab) {\n  if (this.disposing) throw new Error('Browser controller is shutting down')\n  const retry = await this.createTab(privateMode, showWindow)\n  tab = windowInfo.tabs.get(retry.tabId)\n}\nif (!tab) throw new Error('Tab creation failed; webContents was destroyed immediately')\nreturn { tabId: freshTabId, tab }","handlingStrategy":"try-catch","validationCode":"// Retry once when a freshly created tab is missing due to a destruction race\nasync function createTabStable(controller, windowInfo, privateMode, showWindow) {\n  const { tabId } = await controller.createTab(privateMode, showWindow)\n  let tab = windowInfo.tabs.get(tabId)\n  if (!tab && !controller.disposing) {\n    const retry = await controller.createTab(privateMode, showWindow)\n    tab = windowInfo.tabs.get(retry.tabId)\n  }\n  if (!tab) throw new Error('tab creation failed; webContents destroyed immediately')\n  return tab\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await createTabStable(controller, windowInfo, privateMode, showWindow)\n} catch (e) {\n  if (controller.disposing) throw new Error('browser controller shutting down')\n  throw e\n}","preventionTips":["Avoid browser-tool calls during shutdown; gate with an isDisposing flag.","Serialize createTab per window to reduce destruction races.","Investigate GPU/sandbox crashes if creation consistently fails."],"tags":["electron","browser-tool","race","tabs","webcontents"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}