Desktop broadcasts flood stderr while the main Renderer frame is unavailable
- Dominant language
- TypeScript
- Stars
- 5.4k
- Forks
- 502
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 715
Description
## What happened
While the Desktop main Renderer is being replaced after a reload, navigation, or renderer-process loss, main-process broadcasts can repeatedly print:
```text
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:
```ts
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):
1. Run Desktop from source with an active Session so Runtime Host/session events continue to be published.
2. Reload the main Renderer or trigger renderer-process recovery while events are being published.
3. Observe repeated `Error sending from webFrameMain` entries in the main-process output.
Deterministic Electron 43.4.1 verification:
1. Create a hidden `BrowserWindow` and load a page.
2. Continuously call `window.webContents.send(...)`.
3. Call `window.webContents.forcefullyCrashRenderer()`, await `render-process-gone`, and keep sending until a replacement page has loaded.
4. 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.
Contributor guide
Assessment
This issue has not been assessed yet.