Repository navigation
Description
Preflight Checklist
- I have read the Contributing Guidelines for this project.
- I agree to follow the Code of Conduct that this project adheres to.
- I have searched the issue tracker for a bug report that matches the one I want to file, without success.
Electron Version
41.5.0 (also reproduced on 43.4.1)
What operating system(s) are you using?
macOS
Operating System Version
macOS 26.6.2
What arch are you using?
arm64 (including Apple Silicon)
Last Known Working Electron version
Unknown — the affected code path was introduced with the cppgc/Oilpan migration of electron::api::Debugger
Expected Behavior
Attaching webContents.debugger to a WebContents (in our case, <webview> guest contents), receiving CDP notifications, and later letting the WebContents be destroyed should never crash the browser process, regardless of GC timing.
Actual Behavior
The browser process segfaults (EXC_BAD_ACCESS / KERN_INVALID_ADDRESS at 0x10/0x18 — a null/dangling pointer plus member offset) on CrBrowserMain. We collected three crashes in one day across two Electron versions (41.5.0 and 43.4.1), in two distinct but related signatures.
Signature A (2 of 3 crashes) — inbound CDP notification dispatched to a freed client (symbolicated from macOS .ips via breakpad symbols, Electron 41.5.0):
0 content::DevToolsSession::DispatchProtocolNotification(...)
1 blink::mojom::DevToolsSessionHostStubDispatch::Accept(...)
2 mojo::InterfaceEndpointClient::HandleIncomingMessageThunk::Accept(mojo::Message*)
3 mojo::MessageDispatcher::Accept(mojo::Message*)
4 mojo::InterfaceEndpointClient::HandleIncomingMessage(mojo::Message*)
5 IPC::ChannelAssociatedGroupController::AcceptOnEndpointThread(...)
6 base::TaskAnnotator::RunTaskImpl(base::PendingTask&)
7 base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork()
8 base::MessagePumpCFRunLoopBase::RunWorkSource(void*)
... CFRunLoop / NSApplication run loop ...
On 43.4.1 the same crash appears with frame 0 in content::DevToolsSession::DispatchProtocolResponseOrNotification(content::DevToolsAgentHostClient*, ...).
Signature B (1 of 3) — WebContents teardown force-detaches sessions into a freed client (Electron 41.5.0):
0 content::DevToolsAgentHostImpl::ForceDetachAllSessionsImpl()
1 content::RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone()
2 content::RenderFrameDevToolsAgentHost::FrameDeleted(...)
3 content::WebContentsImpl::WebContentsObserverList::NotifyObservers(...)
4 content::WebContentsImpl::OnFrameTreeNodeDestroyed(content::FrameTreeNode*)
5 content::FrameTree::Shutdown()
6 content::WebContentsImpl::~WebContentsImpl()
7 content::WebContentsImpl::WebContentsTreeNode::DestroyOwnedInnerWebContents(...)
8 content::WebContentsImpl::WebContentsTreeNode::DetachInnerWebContents(...)
9 content::WebContentsImpl::WebContentsTreeNode::OnFrameTreeNodeDestroyed(...)
10 content::FrameTreeNode::~FrameTreeNode()
11 content::RenderFrameHostImpl::ResetChildren()
12 content::FrameTree::Shutdown()
13 content::WebContentsImpl::~WebContentsImpl()
14 electron::InspectableWebContents::~InspectableWebContents()
15 electron::api::WebContents::~WebContents()
16 electron::api::WebContents::DeleteThisIfAlive()
17 base::TaskAnnotator::RunTaskImpl(base::PendingTask&)
... message pump ...
Testcase Gist URL
No response
Additional Information
Root-cause analysis (from reading the v41.5.0 / main sources; we do not have a minimal repro yet — the crash depends on GC timing, which matches its "random, tens of minutes apart" occurrence in production):
electron::api::Debugger is now a cppgc/Oilpan-managed gin::Wrappable, but it registers itself with content as a raw content::DevToolsAgentHostClient* via agent_host_->AttachClient(this):
https://github.com/electron/electron/blob/v41.5.0/shell/browser/api/electron_api_debugger.cc#L119
Its destructor never detaches that client:
https://github.com/electron/electron/blob/v41.5.0/shell/browser/api/electron_api_debugger.cc#L35
Debugger::~Debugger() = default;DetachClient is only called from the explicit JS-facing Debugger::Detach():
https://github.com/electron/electron/blob/v41.5.0/shell/browser/api/electron_api_debugger.cc#L126-L131
The only strong root keeping an attached Debugger alive is the owning api::WebContents's member:
https://github.com/electron/electron/blob/v41.5.0/shell/browser/api/electron_api_web_contents.h#L783
cppgc::Persistent<api::Debugger> debugger_;So once the api::WebContents wrapper goes away (e.g. <webview> guest destruction via the posted DeleteThisIfAlive() task, as in Signature B), nothing on the content side owns or pins the still-attached client. When cppgc sweeps the Debugger while DevToolsSession/DevToolsAgentHost still hold the raw client pointer:
- the next CDP notification from the renderer dispatches into freed memory (Signature A — easy to hit with chatty domains like
Network.enable/Log.enable/Page.startScreencast), or - a later WebContents-tree teardown reaches
ForceDetachAllSessionsImpl(), which invokesclient->AgentHostClosed()on freed memory (Signature B). Sweep order between theapi::WebContentsand itsDebuggerin the same GC cycle is unspecified, so the Debugger can be gone by the time the WebContents teardown runs.
The bug is still present on main — Debugger::~Debugger() = default;:
https://github.com/electron/electron/blob/main/shell/browser/api/electron_api_debugger.cc#L36
Before the cppgc migration this was masked because the wrappable's deterministic destruction paths differed; with Oilpan, destruction is decoupled from the last use, so the raw client pointer in content outlives the object.
Suggested fix: ensure the client is detached before the object can be reclaimed — e.g. call agent_host_->DetachClient(this) in ~Debugger() (mirroring Detach()), and/or detach in a WebContentsDestroyed() observer hook so the session never survives its client.
Context in which we hit this: our app attaches webContents.debugger.attach("1.3") to <webview> guest WebContents and enables Runtime/Network/Log/Accessibility (constant notification stream). Guests are routinely destroyed and recreated. Crashes appear minutes to tens of minutes later, aligned with GC pressure rather than any user action. Happy to provide the full .ips reports or symbolicated dumps on request.
Activity
github-actions commentedon Sep 1, 2026
Hello @sethwebster. Thanks for taking the time to open an issue and helping to make Electron better!
This issue was automatically closed because its body does not follow any of this repository's issue templates. Maintainers rely on the sections in those templates to triage issues, and issues that are missing them usually cannot be acted upon.
To get this issue triaged, please either:
Pin the preview CDP debugger so garbage collection cannot crash the app
Closing an approval no longer crashes Pane: documents get Pane's them…