Repository navigation
[Bug]: macOS main process aborts with V8 OOM in JSON.stringify while relaying preview CDP events (SIGTRAP) #20395
Description
Before submitting
- I searched existing issues and did not find a duplicate. [Bug]: Desktop main process SIGTRAPs during preview_open webview attach / debugger.attach #8332 (SIGTRAP during
debugger.attach) and [Bug]: macOS main-process crash in accessibility line-range handling (SIGTRAP) #17476 (accessibility bounds check) also end in aCrBrowserMainSIGTRAP, but their native stacks differ. The closest is the SIGSEGV inDevToolsSession::DispatchProtocolNotificationposted as a comment on [Bug]: Desktop main process SIGTRAPs during preview_open webview attach / debugger.attach #8332. This crash goes through the same dispatch path but fails differently. - I included enough detail to investigate. I don't have a deterministic repro yet.
Area
apps/desktop (preview CDP relay, main process)
Steps to reproduce
The whole desktop app crashed twice in 42 seconds while an agent drove the built-in browser against a local React + Vite dev app. In both cases:
- The agent calls
preview_openonhttp://localhost:5174/query. The tab opens hidden (visible: false), and the call succeeds. - The agent calls
preview_evaluatewith a short DOM query. The second crash's expression also waits 3 seconds first. - The main process aborts 4 to 9 seconds later. That
preview_evaluatecall never gets a response.
The second crash happened 21 seconds after relaunch, so this is not a slow leak over a long session. A single large message seems to be enough.
Timeline from the traces (local time, -0700):
| Crash 1 | Crash 2 | |
|---|---|---|
| App launched | 15:07:43 | 15:40:20 |
| Last completed preview call | preview_snapshot (includeImage: true), 15:39:49 |
preview_open, 15:40:28 to 15:40:30 |
| In-flight call | preview_evaluate, sent 15:39:54 |
preview_evaluate, sent 15:40:32 |
| Crash | 15:39:58 | 15:40:41 |
Before crash 1, a preview_click on the same page also timed out after 15000 ms. The page was busy during that run.
Expected behavior
A large CDP event from a preview tab should be dropped, truncated, or turned into a typed error. It should not abort the desktop process.
Actual behavior
V8 hits a fatal out-of-memory error inside JSON.stringify in the main process and aborts with EXC_BREAKPOINT (SIGTRAP). The desktop window and the bundled server go down together, and every running agent thread loses its turn.
Impact
Blocks work. The agent can't do browser testing on this page, and each crash takes down every other thread in the app.
Version or commit
- T3 Code Nightly
0.0.46-nightly.20261015.3171 - Electron Framework
44.4.5, UUID4C4C44ED-5555-3144-A1CB-427AA45E6F33(same build as [Bug]: macOS main-process crash in accessibility line-range handling (SIGTRAP) #17476)
Environment
macOS 27.0.1 (26A434), Apple silicon (Mac16,7). The app was in the foreground.
Logs or stack traces
I symbolicated both reports with the official electron-v44.4.5-darwin-arm64-symbols.zip. Both crashes have the same stack. Crash 2, thread 0 (CrBrowserMain):
0 partition_alloc::internal::OnNoMemoryInternal(unsigned long) +0x18
2 partition_alloc::internal::OnNoMemory(unsigned long)
3 partition_alloc::internal::OnErrnoNoMem()
5 v8::Utils::ReportOOMFailure(...)
6 v8::internal::V8::FatalProcessOutOfMemory(...)
7 v8::internal::Zone::Expand(unsigned long)
8 v8::internal::Zone::ExpandAndAllocate(unsigned long)
9 v8::internal::OutBuffer<unsigned short>::Extend(unsigned long)
10 v8::internal::FastJsonStringifier<unsigned short>::SerializeObject(...)
11 v8::internal::FastJsonStringifier<unsigned short>::ResumeFrom<unsigned char>(...)
12 v8::internal::JsonStringify(...)
13 v8::internal::Builtin_JsonStringify(...)
14-19 <JIT JavaScript frames>
20 v8::internal::Execution::Call(...)
21 v8::Function::Call(...)
22 node::InternalMakeCallback(...)
24 gin_helper::internal::CallMethodWithArgs(...)
25 gin_helper::EmitEvent<...>
26 gin_helper::EventEmitterMixin<electron::api::Debugger>::Emit<...>
27 electron::api::Debugger::DispatchProtocolMessage(...)
28 content::DevToolsSession::DispatchProtocolResponseOrNotification(...)
29 content::DevToolsSession::DispatchProtocolNotification(...)
30 blink::mojom::DevToolsSessionHostStubDispatch::Accept(...)
...
So a CDP notification from the preview tab reaches a webContents.debugger "message" listener, and that listener calls JSON.stringify on a value large enough to exhaust V8's zone. The OOM is a fatal V8 error, so JS can't catch it.
The bundled apps/desktop/dist-electron/main.cjs registers two debugger.on("message", ...) listeners. The control-session listener in PreviewManager.ensureControlSession doesn't stringify. The CDP relay listener (tab.onMessage → relay.event → send) does, for every event it forwards:
const send = (message) => write(JSON.stringify(message));It sends every non-download event once per attached page session, with no size check. That makes the relay the most likely caller. The JIT frames aren't symbolicated, so I can't prove it from the stack alone.
I don't know which CDP event is oversized. The page is a large React app served by the Vite dev server.
Possible fixes, from narrowest to broadest:
- Check the size of
paramsbefore callingJSON.stringifyin the relay, and drop or replace oversized events with a typed notice. - Have the relay forward only the domains the server-side Playwright actually uses.
- Move the serialization out of the main process, so a fatal error kills a utility process instead of the whole app.
I can share both .ips reports and the matching desktop.trace.ndjson / server.trace.ndjson windows.
Activity
juliusmarminge commentedon Oct 16, 2026
Note
Grok responding on behalf of Julius.
This is a bug on current main: the desktop CDP relay serializes every preview event in the Electron main process with no size limit, so one oversized event can take down the whole app.
What main does (
apps/desktop/src/preview/CdpRelay.ts, unchanged since nightly 3171 apart from comment cleanup in #19559):DesktopBrowserHost.attachregisters adebugger.on("message")listener that passes every event torelay.event.relay.eventcallssend = (message) => write(JSON.stringify(message))once per attached page session, for every event, with no size check.Runtime/Networkpayloads from a big Vite dev page all go through this path.The stack shows
JSON.stringifycalled from JS insideDebugger::Emit, so this is our listener, not an Electron bug. It's also a different failure from #8332 (SIGTRAP indebugger.attach) and #9067 (use-after-free, fixed by #9068).Which event is oversized is still a guess. If you can share the
desktop.trace.ndjsonwindow right before either crash, that would help pin it down.Maxymvs commentedon Oct 16, 2026
Thanks for confirming. I pulled the
desktop.trace.ndjsonwindows, but they don't identify the event:PreviewManager.syncWebContentsState,registerWebview,publishFaviconandemitAgentPointer. All succeed in under 1 ms.server.trace.ndjsonhas already rotated past Oct 15, so I can't send the Playwright side for these two crashes.Preview-related spans, local time (-0700). IPC and polling spans are omitted:
Crash 1 (abort at 15:39:58)
Crash 2 (relaunch at 15:40:20, abort at 15:40:41)
To find the event, the relay would need to record something before calling
JSON.stringify, such as the CDP method name and a rough size ofparams. Logging after the call doesn't help because the abort happens inside it. If a nightly adds that logging, I can run the samepreview_open+preview_evaluatesequence against the page again and report the result. It reproduced within about 20 seconds of launch.kestereverts commentedon Oct 20, 2026
Still happening on a newer build: 3 main-process crashes in about 5 minutes on Nightly
0.1.1-nightly.20261020.3411(d36b7da2), 20 October 2026. The machine and Electron binary are the same as in this report.Environment
0.1.1-nightly.20261020.3411, desktop app. This Mac hosts the server, and the user connects from another device, so each crash also takes down the bundled server and every remote thread.44.4.5, UUID4C4C44ED-5555-3144-A1CB-427AA45E6F33. Same binary as this issue and [Bug]: macOS main-process crash in accessibility line-range handling (SIGTRAP) #17476.mcp:tab IDs).0.1.1-nightly.20261019.3360. The app updated to 3411 between 14:21 and 14:42 local time, and the first crash came at 15:07. Nothing between 3360 and 3411 touchesCdpRelay.ts, so the update may not be the cause. The user's heavy browser use today could also explain the timing.Crashes
All three
.ipsreports showEXC_BREAKPOINT (SIGTRAP)on thread 0CrBrowserMain,brk #0at the PC, and the same leading native frames at the same offsets. In crashes 2 and 3, six JIT frames sit abovev8::Function::Call. That is the same shape as the stack in this issue. I have not symbolicated them.Timeline (UTC). Only the preview automation calls are shown:
snapshot13:04:16,snapshot13:05:50,evaluate13:06:20snapshot13:08:42,snapshot13:09:05snapshot13:12:11.ipscapture time)The pattern held in all three: the agent opens a tab,
snapshot/evaluatetime out (PreviewAutomationTimeoutError, 15000 ms), the desktop trace goes quiet for 5–20 s, and the main process aborts. The relaunched session ran agent threads but opened no preview tab, and it has not crashed so far.No Crashpad minidumps were written, and the unified log has no V8 fatal message. The
.ipsfiles are the only native evidence. I can share them.Related: #16567, which covers the snapshot timeouts that come before each crash.
Filed via
t3 triageby Claude Code (Claude Opus 5.5) on behalf of @kestereverts, with their approval.