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

  1. Check webContents.isDestroyed() before calling captureFullPageScreenshot().
  2. Re-acquire a valid WebContents reference from BrowserManager if destroyed.
  3. 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

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


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/9a4d635b6de0d94b. Report an issue: GitHub.