Skip to content

Desktop main process runs out of V8 heap about 60 seconds into an agent preview recording #30735

Open

Description

@DrBushyTop

What happened

The desktop app crashed twice in eight minutes while agents were using the built-in browser. Each crash took down the bundled server and every running thread, and the machine stopped answering as a remote environment until the app was relaunched. The second crash came 108 seconds after relaunch, once the agents had resumed.

Diagnosis

Both crashes are a fatal V8 out-of-memory error in JSON.stringify, running in the Electron debugger "message" listener while it handles a Page.screencastFrame event. Both came 62 to 63 seconds after preview_recording_start on a recording that was never stopped. All six recordings stopped after 10 to 17 seconds finished normally.

This is not the oversized message from #20395. #23461 is in the crashing build, and a single JPEG frame is far under its 64 Mi limit. The failed allocation is an ordinary one, which suggests the heap was already full.

From reading the source at 322f654, not measured at runtime, the likely cause is an unbounded backlog of frames:

  1. ServerBrowser.startRecording acks each frame only after encoding it, which should limit Chromium to a few unacked frames. It uses everyNthFrame: 1, JPEG quality 90, and no maximum size.
  2. Manager.ts:1068 acks every Page.screencastFrame immediately on the control session. The relay shares that same debugger (Manager.ts:2288), so frames keep coming whether or not the server has kept up.
  3. CdpRelay stringifies each frame in the main process and DesktopBrowserHost.ts:107 queues it on PubSub.unbounded, so the backlog stays on the main process V8 heap.

The press timeout and host disconnect about 16 seconds before each crash would fit a main process stalled in GC. That is an inference. I have no heap numbers from the minute before either crash.

Steps to reproduce

Seen twice in real use. Not yet reduced to a minimal repro:

  1. Use the desktop app with its bundled server.
  2. Have an agent preview_open a local web app and preview_resize to 1280×800.
  3. Call preview_recording_start.
  4. Keep driving the page with preview_act and preview_evaluate, without calling preview_recording_stop.
  5. About 30 seconds in, preview_act stops answering and times out after 15 seconds. The app aborts at about 60 seconds.

Version

0.1.7-nightly.20261104.4151 (Electron 44.10.1). No preview or relay changes in 4165, the current main.

Environment

macOS 26.3.1 (25D2128), MacBookPro18,4, 32 GB RAM, Node 26.12.1, Claude Code and Codex agents

Evidence

Same stack in both crashes, symbolicated with electron-v44.10.1-darwin-arm64-symbols.zip (UUID 4C4C442C-5555-3144-A157-FFC8A432CBF6 matches):

0  partition_alloc::internal::OnNoMemoryInternal
5  v8::Utils::ReportOOMFailure
6  v8::internal::V8::FatalProcessOutOfMemory
8  v8::internal::FactoryBase<v8::internal::Factory>::NewRawOneByteString
9  v8::internal::JsonStringify
10 v8::internal::Builtin_JsonStringify
11-17 <JIT JavaScript>
22 gin_helper::internal::CallMethodWithArgs
24 gin_helper::EventEmitterMixin<electron::api::Debugger>::Emit
25 electron::api::Debugger::DispatchProtocolMessage
26 content::DevToolsSession::DispatchProtocolMessageToClient
28 content::protocol::Page::Frontend::ScreencastFrame
29 content::protocol::PageHandler::ScreencastFrameEncoded

Timeline, seconds after preview_recording_start:

           crash 1   crash 2
press sent    +31       +30     (inferred from the timeout)
press fails   +46       +45     "Preview automation press timed out after 15000ms"
host lost     +46       +45     "No preview automation host is available"
abort         +62       +63     EXC_BREAKPOINT (SIGTRAP) on CrBrowserMain

Main process working set was 180-270 MB in five-minute samples up to about three minutes before crash 1, so this does not look like a slow leak.

Related issues

#27499 has this exact stack and #20395 the same relay path, both closed by the #23461 size limit, which this build already has. #26683: recording frame rate.

Fix applied or workaround

Relaunched the app. Keeping recordings short should avoid it: every recording under 20 seconds finished normally. Updating to 0.1.8-nightly.20261104.4165 does not change this code.

Filed by

claude (opus-5-5) via t3 triage playbook

Activity

  1. al0x99 commented on Nov 6, 2026

    @al0x99

    Same crash on 0.1.8-nightly.20261106.4262, much earlier in the recording

    Two crashes on macOS with the same stack as this report, but only 15 and 21 seconds after recording started. That means the workaround above, keeping recordings under 20 seconds, is not reliably safe.

    Crash 1 Crash 2
    preview_recording_start returned 15:39:27.8 15:40:54.7
    Process aborted (EXC_BREAKPOINT on CrBrowserMain) 15:39:42.6 15:41:15.9
    Time since recording started 14.8 s 21.1 s
    Main process uptime about 6 h 72 s

    Stack. Symbolicated both reports with electron-v44.10.1-darwin-arm64-symbols.zip (UUID 4C4C442C-5555-3144-A157-FFC8A432CBF6 matches). The path is the same in both:

    partition_alloc::internal::OnNoMemoryInternal
    v8::internal::V8::FatalProcessOutOfMemory
    v8::internal::HeapAllocator::AllocateRawSlowPath
    v8::internal::FactoryBase<v8::internal::Factory>::NewRawOneByteString
    v8::internal::JsonStringify
    <JIT JavaScript>
    electron::api::Debugger::DispatchProtocolMessage
    content::protocol::Page::Frontend::ScreencastFrame
    content::protocol::PageHandler::ScreencastFrameEncoded
    

    What differs from the original report

    • Page activity. Right after starting the recording, the agent called a page function that starts a continuous animation. In this report the page was only driven by agent actions and the abort came at about 62 seconds. PR fix(desktop): bound screencast delivery to slow consumers #28518 describes a similar case: an animation evaluated during recording and a crash about 21 seconds after start. Time to crash may therefore depend on frame production rate rather than recording length. This is an inference, I did not measure frame rate or frame size.
    • Not a slow leak. The second process had been running for only 72 seconds.
    • The crash can repeat after relaunch. Both recordings came from the same Codex thread, which repeated the step after the app came back. The second crash took down every running thread again. A later preview_recording_stop returned No recording is active for this thread.
    • Not system memory pressure. There is no Jetsam event at either crash time. The one earlier Jetsam event, at 15:36:53, did not kill T3 Code.

    Current code. At 6577c6b61851 (the 4275 nightly and the main tip when checked), apps/desktop/src/preview/Manager.ts still acks Page.screencastFrame from the control session, and DesktopBrowserHost.ts still uses PubSub.unbounded for the outbox. The commits between 4262 and 4275 show no preview changes by title, so I expect 4275 to behave the same, but I have not run it.

    Limits of this data. I have no main process heap samples, frame sizes or viewport size, and I have no minimal repro. I did not test the patch in #28518.

    Environment. Mac17,8, 64 GB RAM, macOS 27.3 (26C5035r), Electron 44.10.1, browserRecordingFrameRate 30, several agent threads running at the same time.

    Suggestion. Until a fix lands, it would help if agents were warned about recording pages with continuous animation.

  2. mohammedi-haroune commented on Nov 6, 2026

    @mohammedi-haroune

    Likely same crash on 0.1.8-nightly.20261106.4262, on a 16 GB MacBook Air (no native stack)

    The desktop main process died 47 seconds after an agent started a preview recording. This was the first crash on this machine, after about 12.6 hours of uptime.

    Time (local) Event
    21:34:18.3 preview_recording_start returned
    21:34:20.8 preview_navigate started, took 8.7 s
    21:34:30.4 preview_evaluate started
    21:34:45.5 preview_evaluate failed: Preview automation evaluate timed out after 15000ms (+27 s)
    21:34:52.8 Next preview_evaluate started, never answered
    21:35:05.3 Main process exited: exited due to SIGTRAP, sent by exc handler (+47 s)
    21:35:06.4 Bundled server shut down cleanly

    No preview_recording_stop appears in the server trace before the crash.

    Limits of this data. I have no stack: macOS logged AMFI: Denying core dump, no .ips report was written, and I found no Crashpad dump. The match rests on timing and circumstances, so the V8 OOM is not confirmed here. I have no main process heap samples or frame sizes, and I don't know whether the page was animating.

    Other observations

    • The last renderer samples I read (up to 21:32:17) were all under 250 MB. They don't cover the main process or the final three minutes.
    • No Jetsam event was logged at crash time. The most recent one was at 16:04 that day.
    • At tag v0.1.8-nightly.20261106.4262, browserRecordingFrameRate reaches only the encoder script. RECORDING_SCREENCAST stays at everyNthFrame: 1, so lowering that setting would not reduce incoming frames.

    Environment. MacBookAir10,1, 16 GB RAM, macOS 15.8.1, Electron 44.10.1, desktop app with bundled server, agent driving the integrated browser.

    Filed by claude (opus-5-5) via t3 triage playbook

  3. aoppenh1-afk commented on Nov 8, 2026

    @aoppenh1-afk

    Confirmed the same symbolicated crash on T3 Code Desktop Nightly 0.1.8-nightly.20261107.4340, commit a9e709f15f825cacac1b6d665fa1920c328d68b7. The user reports one occurrence.

    Environment: Apple silicon Mac15,12, macOS 27.2 (26B123), Electron 44.10.1. The local desktop/server build matches the crash report.

    Timeline on 2026-11-08, UTC:

    • 05:31:09.508: preview_recording_start completed.
    • 05:31:24.824: the following click ended in a 15-second timeout.
    • 05:31:40.213: the following evaluation ended in a 15-second timeout.
    • 05:32:43.038: main process aborted with EXC_BREAKPOINT (SIGTRAP) on CrBrowserMain, about 93.5 seconds after recording started.

    No subsequent preview_recording_stop call appears in the retained server trace before the crash. The desktop app relaunched at 05:32:52.376.

    Symbolicated the supplied crash report using official Electron 44.10.1 ARM64 Breakpad symbols. Module UUID matches 4C4C442C-5555-3144-A157-FFC8A432CBF6:

    0  partition_alloc::internal::OnNoMemoryInternal
    4  V8OOMErrorCallback
    5  v8::Utils::ReportOOMFailure
    6  v8::internal::V8::FatalProcessOutOfMemory
    8  FactoryBase<Factory>::NewRawOneByteString
    9  v8::internal::JsonStringify
    10 v8::internal::Builtin_JsonStringify
    24 electron::api::Debugger::DispatchProtocolMessage
    27 content::protocol::Page::Frontend::ScreencastFrame
    28 content::protocol::PageHandler::ScreencastFrameEncoded
    

    The affected source still has the per-message size guard, immediate control-session frame acknowledgements, and unbounded desktop event outbox. This confirms the same failure path, but does not measure the proposed queue-backlog mechanism. No deterministic repro or main-process heap capture is available. No fix was applied or tested. PR #28518 is still open; the changes in nightly 0.1.8-nightly.20261108.4353 do not modify this recording/relay path.

    Prepared by Codex, gpt-6.1-sol, through the T3 Code triage playbook.

  4. mindtheflo commented on Nov 8, 2026

    @mindtheflo

    Same crash on 0.1.7 (Beta channel), 3 times in one day, including a variant that aborts in a GC worker

    Three main process aborts on 2026-11-08 on T3 Code (Beta) 0.1.7, so this is not limited to the 0.1.8 nightlies. All three symbolicated with electron-v44.10.1-darwin-arm64-symbols.zip (module UUID 4C4C442C-5555-3144-A157-FFC8A432CBF6 matches).

    Crash (UTC) Thread Main process uptime Path
    10:47:08 CrBrowserMain about 3 days JsonStringify <- Debugger::DispatchProtocolMessage <- Page::Frontend::ScreencastFrame
    10:50:50 CrBrowserMain 3 min 21 s (relaunch after the first crash) same as above
    12:15:37 V8Worker 14 min FatalProcessOutOfMemory <- LiveObjectVisitor::VisitMarkedObjectsNoFail<EvacuateNewSpaceVisitor> <- Evacuator::EvacuatePage <- PageEvacuationJob::Run

    The third one is new for this thread: the heap ran out during a young-generation evacuation on a GC worker, not inside JSON.stringify. Same OOM, different place where it tips over, so a crash signature match on JsonStringify alone will miss some of these.

    partition_alloc::internal::OnNoMemoryInternal
    partition_alloc::internal::OnNoMemory
    v8::Utils::ReportOOMFailure
    v8::internal::V8::FatalProcessOutOfMemory
    <inlined>
    v8::internal::LiveObjectVisitor::VisitMarkedObjectsNoFail<v8::internal::EvacuateNewSpaceVisitor>
    v8::internal::Evacuator::EvacuatePage
    v8::internal::PageEvacuationJob::Run
    v8::platform::DefaultJobWorker::Run
    node::(anonymous namespace)::PlatformWorkerThread
    

    Lead-up, from the server trace. Same pattern as the other reports: preview automation calls start timing out well before the abort, which fits a main process busy with a frame backlog.

    • 12:06:48 and 12:10:48: recordingStop timed out after 120000ms (twice; a recording could not be stopped).
    • 12:12:42: PreviewToolkit.claimRecording (a new recording).
    • 12:13:14 to 12:15:10: waitFor (45 s), type (15 s), click (15 s) timeouts, then No preview automation host is available.
    • 12:15:35: a preview tab reloads (dom-ready, registerWebview); abort 2 s later.
    • Before the 10:47 and 10:50 crashes: a run of 15 s evaluate / navigate / type / click / status timeouts. The retained trace starts at 10:42, so I cannot confirm when that recording started.

    The recordingStop 120 s timeouts are the most useful extra signal: once the main process is backed up, the agent cannot stop the recording that is filling it, so it keeps going until the heap runs out.

    Not memory pressure from outside. No Jetsam events at the crash times. Preview and main renderer working sets stayed under about 1 GB.

    Environment. Mac15,7, macOS 26.5 (25F71), Electron 44.10.1, T3 Code (Beta) 0.1.7, bundled server (no background service), several agent threads driving preview tabs at once.

    Limits. No main process heap samples or frame sizes, and no minimal repro. I did not test #28518.

    Filed by claude (opus-5-5)

  5. deleted a comment from Wladefant on Nov 8, 2026
  6. yeqiuqiu123 commented on Nov 10, 2026

    @yeqiuqiu123

    Same crash on Linux, 0.1.8 Beta; the change in #28518 fixes it for us

    Environment: T3 Code (Beta) 0.1.8, Linux x86_64 (kernel 7.0), Electron 44.10.1, 64 GB RAM. About ten agent threads were using the built-in preview heavily for screenshots and recordings at the same time.

    Crashes: The desktop main process aborted twice on 2026-11-10, at about 15:15 and 15:25 UTC. Each time it took the bundled server and every running thread with it. The second abort logged:

    ... last resort; GC in old space requested
    electron/shell/common/node_bindings.cc:195] OOM error in V8: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
    

    64 GB of system RAM made no difference. The ceiling is the main process V8 heap, and the frame backlog has no bound.

    Measured backlog (unpatched 0.1.8 relay): I used an isolated Electron 44.10.1 fixture with an animated page. Within about 4 seconds the native screencast produced 120 frames, and the relay had queued 240 frames, about 14.4 MB of payload. Nothing drained that queue.

    With the #28518 change: I ported the per-consumer bound from #28518 to the installed 0.1.8 main.cjs: at most one unacknowledged frame per requesting session, with overlapping starts tracked as pending or committed. Only the CdpRelay and DesktopBrowserHost sections changed.

    • The CdpRelay and DesktopBrowserHost test files pass, 61 of 61. Without the change, 12 of those tests fail.
    • A stress run delivered 4,746 native frames. The relay queue stayed at 1 frame, and the fixture's heapUsed stayed flat at 3.6 to 4.6 MB.
    • The patched app has been running the same multi-agent load without another abort.

    It would help a lot to get #28518 merged and into a nightly. We're happy to test a build on Linux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions