stablyai/orca · error · BrowserError
browser_debugger_detached
browser_debugger_detached
Error message
The active browser tab was closed. Run 'orca tab list' to find remaining tabs.
What it means
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.
Source
Thrown at src/main/browser/cdp-bridge.ts:1052
if (tabs.length === 0) {
throw new BrowserError(
'browser_no_tab',
'No browser tab is open. Use the Orca UI to open a browser tab first.'
)
}
if (tabs.length === 1) {
this.activeWebContentsId = tabs[0][1]
} else {
throw new BrowserError(
'browser_no_tab',
"Multiple browser tabs are open. Run 'orca tab list' and 'orca tab switch --index <n>' to select one."
)
}
const guest = webContents.fromId(this.activeWebContentsId!)
if (!guest || guest.isDestroyed()) {
this.activeWebContentsId = null
throw new BrowserError(
'browser_debugger_detached',
"The active browser tab was closed. Run 'orca tab list' to find remaining tabs."
)
}
return guest
}
private getRegisteredTabs(): Map<string, number> {
// Why: reach into BrowserManager's private tab map since it exposes no public listTabs().
return (this.browserManager as unknown as { webContentsIdByTabId: Map<string, number> })
.webContentsIdByTabId
}
private resolveTabId(webContentsId: number): string {
for (const [tabId, wcId] of this.getRegisteredTabs()) {
if (wcId === webContentsId) {
return tabId
}View on GitHub (pinned to 1136503c6a)
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.
Defensive patterns
Strategy: retry
Validate before calling
const { tabs } = bridge.tabList()
if (tabs.length === 0) {
throw new Error('No live tab available — open one before retrying')
}
await bridge.click(ref) Try / catch
try {
await bridge.click(ref)
} catch (e) {
if (e instanceof BrowserError && e.code === 'browser_debugger_detached' && /closed/.test(e.message)) {
// activeWebContentsId was cleared; re-select from remaining tabs and retry
const { tabs } = bridge.tabList()
if (tabs.length > 0) {
await bridge.tabSwitch(tabs.length - 1)
await bridge.click(ref)
}
}
throw e
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Timing races during tab teardown; renderer crash on a heavy page; user closes the tab at the exact moment a command is issued.
Related errors
- browser_tab_not_found
- Session not found: ${sessionId}
- Could not read watchdog child RSS for PID ${pid}
- Could not read CPU time for PID ${pid}
- Could not inspect process ${pid}: ${raw}
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/ffd5fec15bdee8cc.
Report an issue: GitHub.