{"record":{"id":"ffd5fec15bdee8cc","repo":"stablyai/orca","slug":"browser-debugger-detached","errorCode":"browser_debugger_detached","errorMessage":"The active browser tab was closed. Run 'orca tab list' to find remaining tabs.","messagePattern":"The active browser tab was closed\\. Run 'orca tab list' to find remaining tabs\\.","errorType":"exception","errorClass":"BrowserError","httpStatus":null,"severity":"error","filePath":"src/main/browser/cdp-bridge.ts","lineNumber":1052,"sourceCode":"    if (tabs.length === 0) {\n      throw new BrowserError(\n        'browser_no_tab',\n        'No browser tab is open. Use the Orca UI to open a browser tab first.'\n      )\n    }\n    if (tabs.length === 1) {\n      this.activeWebContentsId = tabs[0][1]\n    } else {\n      throw new BrowserError(\n        'browser_no_tab',\n        \"Multiple browser tabs are open. Run 'orca tab list' and 'orca tab switch --index <n>' to select one.\"\n      )\n    }\n\n    const guest = webContents.fromId(this.activeWebContentsId!)\n    if (!guest || guest.isDestroyed()) {\n      this.activeWebContentsId = null\n      throw new BrowserError(\n        'browser_debugger_detached',\n        \"The active browser tab was closed. Run 'orca tab list' to find remaining tabs.\"\n      )\n    }\n    return guest\n  }\n\n  private getRegisteredTabs(): Map<string, number> {\n    // Why: reach into BrowserManager's private tab map since it exposes no public listTabs().\n    return (this.browserManager as unknown as { webContentsIdByTabId: Map<string, number> })\n      .webContentsIdByTabId\n  }\n\n  private resolveTabId(webContentsId: number): string {\n    for (const [tabId, wcId] of this.getRegisteredTabs()) {\n      if (wcId === webContentsId) {\n        return tabId\n      }","sourceCodeStart":1034,"sourceCodeEnd":1070,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/browser/cdp-bridge.ts#L1034-L1070","documentation":"After auto-selecting the single registered tab, getActiveGuest() calls webContents.fromId() and checks isDestroyed(). If the tab was destroyed between the registry iteration and the fromId call, this is a teardown race — the tab existed in the registry but its renderer was torn down in the gap. activeWebContentsId is reset to null so the next command can attempt re-selection. The message directs the user to tab list to find remaining tabs.","triggerScenarios":"The single open tab is closed or its renderer crashes in the window between getRegisteredTabs() and webContents.fromId(); a process swap invalidates the id mid-call.","commonSituations":"Timing races during tab teardown; renderer crash on a heavy page; user closes the tab at the exact moment a command is issued.","solutions":["Run 'orca tab list' to see if any tabs remain.","Open a new browser tab if none remain, then retry.","Retry the command — the stale activeWebContentsId has been cleared, allowing re-selection."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const { tabs } = bridge.tabList()\nif (tabs.length === 0) {\n  throw new Error('No live tab available — open one before retrying')\n}\nawait bridge.click(ref)","typeGuard":null,"tryCatchPattern":"try {\n  await bridge.click(ref)\n} catch (e) {\n  if (e instanceof BrowserError && e.code === 'browser_debugger_detached' && /closed/.test(e.message)) {\n    // activeWebContentsId was cleared; re-select from remaining tabs and retry\n    const { tabs } = bridge.tabList()\n    if (tabs.length > 0) {\n      await bridge.tabSwitch(tabs.length - 1)\n      await bridge.click(ref)\n    }\n  }\n  throw e\n}","preventionTips":["Treat browser_debugger_detached as recoverable — activeWebContentsId is cleared, so retry can re-select.","Check tabList() after this error to see if any tabs remain.","Open a new tab if none remain before retrying the command."],"tags":["tabs","race-condition","lifecycle","teardown"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}