What happened
While the Desktop main Renderer is being replaced after a reload, navigation, or renderer-process loss, main-process broadcasts can repeatedly print:
Error sending from webFrameMain: Error: Render frame was disposed before WebFrameMain could be accessed
at WebFrameMain.send (node:electron/js2c/browser_init:2:107951)
at WebContents.send (node:electron/js2c/browser_init:2:93999)
Session and transcript activity can turn the short unavailable-frame interval into a large stderr flood. The app remains alive because Electron logs the failure internally, but the useful main-process log is obscured.
safeSendToRenderer() currently treats a non-destroyed WebContents as sendable:
for (const contents of recipients) {
if (!contents.isDestroyed()) contents.send(channel, ...args);
}
That predicate is insufficient: after render-process-gone, the same WebContents reports isDestroyed() === false while its main frame can already be disposed.
Expected behavior: broadcasts should not attempt delivery to a renderer generation that has no live main frame, and renderer recovery should not flood stderr. Events that require resynchronization should still be restored by the existing recovery path once the new renderer is ready.
How to reproduce
Product trigger (timing-dependent):
- Run Desktop from source with an active Session so Runtime Host/session events continue to be published.
- Reload the main Renderer or trigger renderer-process recovery while events are being published.
- Observe repeated
Error sending from webFrameMain entries in the main-process output.
Deterministic Electron 43.4.1 verification:
- Create a hidden
BrowserWindow and load a page.
- Continuously call
window.webContents.send(...).
- Call
window.webContents.forcefullyCrashRenderer(), await render-process-gone, and keep sending until a replacement page has loaded.
- The exact error above is printed repeatedly even though
window.webContents.isDestroyed() is false.
The deterministic run made 9,989 send attempts and reproduced the error flood. Wrapping each webContents.send() call in application-side try/catch caught zero errors: Electron logs these failures internally, so a catch-only change does not address this reproduction.
Environment
- Maka commit:
a9b4f21afb17e0ef310d076382e69ddda3a441d7
- Electron:
43.4.1
- Original observation: Windows source checkout, Desktop,
npm run dev
- Deterministic verification: Linux x86_64 (
7.0.0-30-generic), Desktop under Xvfb
- Node.js:
v26.3.0
Logs, screenshots, or additional context
Relevant current-main boundary: apps/desktop/src/main/main-window.ts:133-138.
Known callers include Session/local-session and browser-state broadcasts. The recovery path observes render-process-gone and may reload the same main BrowserWindow, so WebContents lifetime is not the same as renderer-frame/process lifetime.
This issue is limited to broadcast behavior during the unavailable renderer interval. The cause of any particular renderer crash/reload is separate.
What happened
While the Desktop main Renderer is being replaced after a reload, navigation, or renderer-process loss, main-process broadcasts can repeatedly print:
Session and transcript activity can turn the short unavailable-frame interval into a large stderr flood. The app remains alive because Electron logs the failure internally, but the useful main-process log is obscured.
safeSendToRenderer()currently treats a non-destroyedWebContentsas sendable:That predicate is insufficient: after
render-process-gone, the sameWebContentsreportsisDestroyed() === falsewhile its main frame can already be disposed.Expected behavior: broadcasts should not attempt delivery to a renderer generation that has no live main frame, and renderer recovery should not flood stderr. Events that require resynchronization should still be restored by the existing recovery path once the new renderer is ready.
How to reproduce
Product trigger (timing-dependent):
Error sending from webFrameMainentries in the main-process output.Deterministic Electron 43.4.1 verification:
BrowserWindowand load a page.window.webContents.send(...).window.webContents.forcefullyCrashRenderer(), awaitrender-process-gone, and keep sending until a replacement page has loaded.window.webContents.isDestroyed()isfalse.The deterministic run made 9,989 send attempts and reproduced the error flood. Wrapping each
webContents.send()call in application-sidetry/catchcaught zero errors: Electron logs these failures internally, so a catch-only change does not address this reproduction.Environment
a9b4f21afb17e0ef310d076382e69ddda3a441d743.4.1npm run dev7.0.0-30-generic), Desktop under Xvfbv26.3.0Logs, screenshots, or additional context
Relevant current-main boundary:
apps/desktop/src/main/main-window.ts:133-138.Known callers include Session/local-session and browser-state broadcasts. The recovery path observes
render-process-goneand may reload the same mainBrowserWindow, soWebContentslifetime is not the same as renderer-frame/process lifetime.This issue is limited to broadcast behavior during the unavailable renderer interval. The cause of any particular renderer crash/reload is separate.