Affected area
Desktop app
Installation method
Desktop release
Lody version or commit
0.91.1 (a5e15d1)
Operating system
macOS 26.6.2 arm64 ; (but the defect is tied to Electron Renderer lifecycle timing rather than a specific OS. Validate on the supported desktop OS matrix.)
Agent or runtime
codex-cli 0.153.4
What happened?
Problem
The Electron main process broadcasts loro.status and loro.event to subscribed Renderer WebContents. During a local data-plane socket close or reconnect, Renderer disposal can overlap with one of these broadcasts.
This is a user-visible reliability defect: a normal window close, reload, or Renderer disposal can terminate the desktop process instead of affecting only the disposed Renderer.
Actual behavior
The relay may observe a sender as alive and then call webContents.send() after the Renderer has been disposed. Electron can synchronously throw Object has been destroyed or an equivalent disposed-frame error.
Because the call occurs from an asynchronous socket callback, the exception can escape the relay and cause the Electron main process to exit. The surviving Renderer and IPC path are then lost, and a genuine main-process stack trace may not be available for diagnosis.
What did you expect?
A Renderer disposal racing with Loro delivery should be isolated to that Renderer. Electron should remain running, healthy Renderers should remain responsive, and the Loro data plane should reconnect normally.
A genuine Electron main-process fatal exit should also leave a persisted stack trace for diagnosis, without changing the original exit semantics if diagnostic persistence fails.
How can we reproduce it?
- Start the OSS desktop with a connected local Loro data plane.
- Keep the primary Renderer as the control group and attach a second hidden Renderer.
- Trigger a real local data-plane socket close to simulate network jitter or reconnect.
- Dispose the hidden Renderer after the relay has checked that it is alive but before the
loro.status broadcast is sent.
- Wait for the data plane to reconnect and for a real ping/pong exchange to complete.
- Observe the Electron process, primary Renderer IPC, disposed-target cleanup, and fatal logs.
The same timing can occur nondeterministically when a window is closed or reloaded while the local data plane changes connection state.
How often does it happen?
Sometimes
Relevant log output
Since exit stack was not logged , we might not see any race issues in the current log file .
We should also add the exit stack trace so we can collect more logs to investigate further relevant issues.
Additional context
Scope/acceptance criteria
- A forced disposal/broadcast race does not terminate the Electron main process.
- The disposed Renderer is no longer treated as an active broadcast recipient.
- The primary Renderer and IPC remain responsive after the race.
- The Loro data plane reconnects successfully, verified by an actual ping/pong exchange.
- Live Renderers retain the existing
loro.status and loro.event behavior and payloads.
- Renderer-lifecycle disposal errors are contained without silently hiding unrelated send or serialization failures.
- Genuine main-process fatal exits retain an actionable persisted stack trace.
- The expected Renderer disposal race does not produce a fatal main-process log.
This issue describes observable behavior and acceptance criteria; it does not prescribe a particular guard, exception-handling structure, hook, or data structure.
Compatibility constraints
- Existing Renderer fatal logging must remain behaviorally unchanged.
- Chinese, emoji, and other non-ASCII/Unicode error content must not be corrupted, replaced, or newly escaped.
- The existing IPC channels and payload contracts must remain compatible.
- Local OSS builds must remain local-only and must not require authenticated cloud requests or telemetry for this reliability behavior.
Validation notes
- Unit coverage should deterministically reproduce disposal between the alive check and send, verify stale-sender cleanup, and preserve visibility of unrelated send errors.
- Fatal-log coverage should verify stack persistence and best-effort behavior when the log cannot be written.
- The desktop E2E should use a real Electron main process, preload, Renderer, bundled CLI, and real socket close/reconnect; it should assert process survival, IPC availability, healthy Renderer responsiveness, disposed-sender cleanup, successful ping/pong reconnection, and absence of a fatal log.
- On the reviewed revision, the focused Electron tests passed and
pnpm e2e:check passed.
Before submitting
Affected area
Desktop app
Installation method
Desktop release
Lody version or commit
0.91.1 (a5e15d1)
Operating system
macOS 26.6.2 arm64 ; (but the defect is tied to Electron Renderer lifecycle timing rather than a specific OS. Validate on the supported desktop OS matrix.)
Agent or runtime
codex-cli 0.153.4
What happened?
Problem
The Electron main process broadcasts
loro.statusandloro.eventto subscribed Renderer WebContents. During a local data-plane socket close or reconnect, Renderer disposal can overlap with one of these broadcasts.This is a user-visible reliability defect: a normal window close, reload, or Renderer disposal can terminate the desktop process instead of affecting only the disposed Renderer.
Actual behavior
The relay may observe a sender as alive and then call
webContents.send()after the Renderer has been disposed. Electron can synchronously throwObject has been destroyedor an equivalent disposed-frame error.Because the call occurs from an asynchronous socket callback, the exception can escape the relay and cause the Electron main process to exit. The surviving Renderer and IPC path are then lost, and a genuine main-process stack trace may not be available for diagnosis.
What did you expect?
A Renderer disposal racing with Loro delivery should be isolated to that Renderer. Electron should remain running, healthy Renderers should remain responsive, and the Loro data plane should reconnect normally.
A genuine Electron main-process fatal exit should also leave a persisted stack trace for diagnosis, without changing the original exit semantics if diagnostic persistence fails.
How can we reproduce it?
loro.statusbroadcast is sent.The same timing can occur nondeterministically when a window is closed or reloaded while the local data plane changes connection state.
How often does it happen?
Sometimes
Relevant log output
Additional context
Scope/acceptance criteria
loro.statusandloro.eventbehavior and payloads.This issue describes observable behavior and acceptance criteria; it does not prescribe a particular guard, exception-handling structure, hook, or data structure.
Compatibility constraints
Validation notes
pnpm e2e:checkpassed.Before submitting