stablyai/orca · error · Error
WebContents destroyed
Error message
WebContents destroyed
What it means
The standalone screenshot module (cdp-screenshot.ts) checks webContents.isDestroyed() before attempting captureFullPageScreenshot(). A destroyed WebContents has no live renderer to paint from, so CDP screenshot commands would fail or hang. Unlike the CdpBridge variants, this throws a plain Error (not a BrowserError with a structured code), so callers must match on the message string or check isDestroyed() themselves.
Source
Thrown at src/main/browser/cdp-screenshot.ts:138
return await Promise.race([
webContents.debugger.sendCommand(method, params ?? {}) as Promise<T>,
new Promise<T>((_, reject) => {
timer = setTimeout(() => reject(new Error(timeoutMessage)), SCREENSHOT_TIMEOUT_MS)
})
])
} finally {
if (timer) {
clearTimeout(timer)
}
}
}
export async function captureFullPageScreenshot(
webContents: WebContents,
format: 'png' | 'jpeg' = 'png'
): Promise<{ data: string; format: 'png' | 'jpeg' }> {
if (webContents.isDestroyed()) {
throw new Error('WebContents destroyed')
}
const dbg = webContents.debugger
if (!dbg.isAttached()) {
throw new Error('Debugger not attached')
}
try {
webContents.invalidate()
} catch {
// Some guest teardown paths reject repaint requests. Fall through to CDP.
}
const metrics = await sendCommandWithTimeout<{
cssContentSize?: { width?: number; height?: number }
contentSize?: { width?: number; height?: number }
}>(webContents, 'Page.getLayoutMetrics', undefined, SCREENSHOT_TIMEOUT_MESSAGE)
const clip = getLayoutClip(metrics)
if (!clip) {View on GitHub (pinned to 1136503c6a)
Solutions
- Check webContents.isDestroyed() before calling captureFullPageScreenshot().
- Re-acquire a valid WebContents reference from BrowserManager if destroyed.
- If the tab is gone, open a new one before retrying.
Example fix
// before
const result = await captureFullPageScreenshot(wc)
// after
if (wc.isDestroyed()) {
throw new Error('Cannot screenshot: tab was destroyed')
}
const result = await captureFullPageScreenshot(wc) Defensive patterns
Strategy: validation
Validate before calling
if (webContents.isDestroyed()) {
throw new Error('Cannot screenshot: WebContents was destroyed')
}
const result = await captureFullPageScreenshot(webContents, format) Type guard
function isLiveWebContents(wc: Electron.WebContents): boolean {
return !wc.isDestroyed()
} Try / catch
try {
return await captureFullPageScreenshot(wc, format)
} catch (e) {
if (e instanceof Error && e.message === 'WebContents destroyed') {
// re-acquire WebContents or report tab-closed to user
}
throw e
} Prevention
- Check webContents.isDestroyed() before calling captureFullPageScreenshot().
- Register a 'destroyed' listener to proactively invalidate references.
- Note this throws a plain Error, not a BrowserError — match on message, not code.
When it happens
Trigger: Calling captureFullPageScreenshot() after the tab was closed, the renderer crashed, or a process swap replaced the WebContents identity.
Common situations: Tab closed between the screenshot request and execution; renderer OOM; screenshotting during teardown; stale WebContents reference from a prior navigation.
Related errors
- browser_tab_not_found
- [plain-node-entry-guard] "${entryName}" reaches chunk "${chu
- Electron did not expose GC; keep --js-flags=--expose-gc in t
- Electron trial failed (${result.error?.message ?? result.sig
- Electron trial did not report a result (status ${result.stat
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/9a4d635b6de0d94b.
Report an issue: GitHub.