Skip to content

[Bug]: Desktop main process segfaults when a preview tab's renderer dies during an agent snapshot screenshot #24026

Open

Description

@Batuhan1999

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/desktop

Steps to reproduce

Seen once in normal use. These steps follow the crash's timeline:

  1. Have an agent drive a desktop preview tab (preview_open, then preview_wait_for / preview_snapshot / preview_evaluate) on a page whose renderer becomes busy. Here it was a local React 19.2 dev build whose memory grew to about 2 GB.
  2. The tab stops answering: waitFor, snapshot, evaluate and navigate all time out (PreviewAutomationTimeoutError).
  3. The tab's renderer later dies (here: out of memory).
  4. The desktop main process crashes immediately (SIGSEGV at 0x0).

A likely deterministic repro, which I have not verified: start preview_snapshot on a tab whose renderer is stalled, then kill that renderer process while the CDP Page.captureScreenshot is still pending.

Expected behavior

The preview tab reports its crashed renderer and the app keeps running.

Actual behavior

The Electron browser process dies with EXC_BAD_ACCESS (SIGSEGV) KERN_INVALID_ADDRESS at 0x0 on CrBrowserMain, which takes down the bundled server too. The crashed renderer itself aborted out of memory 0–1 s earlier (same capture time).

Symbolicated with Electron's official v44.4.5 darwin-arm64 dSYM (Electron Framework UUID 4C4C44ED-5555-3144-A1CB-427AA45E6F33 matches the crash report):

Thread 0 Crashed:: CrBrowserMain
content::RenderWidgetHostImpl::OnSnapshotFromSurfaceReceived(int, int, base::expected<viz::CopyOutputBitmapWithMetadata, content::CopyFromSurfaceError> const&)  render_widget_host_impl.cc:3916
content::RenderWidgetHostViewChildFrame::CopyFromSurface(...)::$_0  render_widget_host_view_child_frame.cc:1020   ← <webview> guest
base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork()
base::MessagePumpCFRunLoopBase::RunWorkSource(void*)
...
content::BrowserMainLoop::RunMainMessageLoop()

Crashed preview renderer, same moment:

CrRendererMain  EXC_BREAKPOINT
partition_alloc::internal::OnNoMemoryInternal(unsigned long)
blink::Partitions::HandleOutOfMemory(unsigned long)
...
blink::SerializedScriptValue::Create()
blink::PerformanceMeasure::Create(...)
blink::Performance::MeasureWithDetail(...)
v8_inspector::TaskInfo::runTask(...)          ← React dev "Performance Tracks"
blink::MessagePort::DispatchMessageEvent(...)

Impact

Blocks work completely (the whole app and every thread go down)

Version or commit

0.0.46-nightly.20261011.2994 (Electron 44.4.5 / Chrome 152.0.7977.130). The code path is unchanged on main @ d7a8d6f (Electron 44.9.3).

Environment

macOS 15.7.5 (24G624), Apple Silicon. Claude Code provider driving desktop preview tabs over the preview MCP tools.

Root cause (upstream Chromium)

OnSnapshotFromSurfaceReceived is the callback for RenderWidgetHostImpl::GetSnapshotFromBrowser(..., from_surface=true), which DevTools uses for Page.captureScreenshot. When a copy fails, Chrome ≤156 retries with GetView()->CopyFromSurface(...) and never null-checks GetView(). Once the renderer is gone, the view is null, and the retry dereferences it.

Fixed upstream in Chromium f49f3e5, "Fix null ptr deref crash in Page.captureScreenshot when renderer gone" (crbug.com/567321049, CL 8485185, first in 157.0.8083.0). It adds && GetView() to the retry condition. No stable Electron has it: 44-x-y is on Chromium 152 and 45-x-y on 156. Only Electron main and the 46 nightlies (158) include it.

Why T3 hits it

Desktop preview snapshots send Page.captureScreenshot to the guest through the CDP relay (ServerBrowserPage.captureViewport → DesktopBrowserHost → sendPageCommand → debugger.sendCommand). A CDP command can't be cancelled. When the snapshot deadline passes on a stalled tab, the capture stays pending in Chromium, and each retried snapshot can leave another one behind. Any capture still pending when the renderer dies crashes the app.

Electron's own webContents.capturePage() isn't affected. It null-checks the view and never retries through GetView(). It does behave differently on hidden or cold guests, which is why #19931 and #19913 handle those cases carefully.

Possible directions (for maintainers to choose)

  1. Answer Page.captureScreenshot in sendPageCommand with capturePage() until Electron ships Chromium ≥157. This removes the crash path but has to preserve the scale, zoom, scroll and hidden-tab behavior from fix(server): desktop-drawn preview snapshots match the page's scale and viewport #19913 and fix(desktop): capture fresh pixels from background previews #19931. It is a larger, riskier change, temporary until the Electron bump.
  2. Allow only one pending CDP screenshot per tab, and don't start another while an earlier one is still unresolved. This narrows the window but doesn't eliminate it. It is small (a per-tab pending counter in the relay plus one focused test) and doesn't change how screenshots are taken. I'm happy to send a PR for this once the direction is agreed.
  3. Request an Electron backport of f49f3e5 to 44-x-y and 45-x-y. Electron's patch is a one-line cherry-pick; there's a precedent in fix: cherry-pick 431b8be6a111 from chromium electron/electron#54912.

Logs

Timeline from ~/.t3/userdata/logs (server and desktop traces), local time:

09:50:27  preview_open http://localhost:5173/chat  → guest renderer created
09:50:59  PreviewAutomationTimeoutError: waitFor timed out after 60000ms
09:52:01  PreviewAutomationTimeoutError: waitFor timed out after 60000ms
09:53:07  PreviewAutomationTimeoutError: snapshot timed out after 15000ms
09:53:25  PreviewAutomationTimeoutError: evaluate timed out after 15000ms
09:53:47  PreviewAutomationTimeoutError: navigate timed out after 15000ms
09:54:06  preview_open (second tab)  → its renderer grows 243 MB → 2.1 GB by 10:03
10:05:17.034  PreviewManager.close
10:05:17.27   main process SIGSEGV (above); preview renderer OOM abort at the same capture time

Related but different crashes: #8332 and #9067 (debugger use-after-free, fixed in #9068), and #20395 (relay JSON.stringify OOM, open PR #23461). #21980 covers crashed-tab recovery.

Workaround

Avoid agent snapshots on preview tabs that have stopped responding; close them instead. Keep heavy dev-mode pages out of long agent preview sessions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions