widget.ts calls ev.preventDefault() for every lotus action and forwards it to lazyActions. When a handler has been removed (any of the start* teardowns), super.emit returns false and the request is appended to eventBacklogs forever — never replied to, so the host's transport.send pends to its 10 s timeout, and the map grows without bound for the lifetime of the page. On the next registration the whole backlog replays synchronously: each handler calls w.api.transport.reply(ev.detail, {}) for long-expired request ids (the host logs "response to unknown request" for each), and for focus_participant every queued pin is applied in sequence so the oldest intent can end up overwritten by whatever arrived last, silently. Nothing ever prunes the backlog.
How to trigger
unmount the call view (handlers off), send several io.lotus.focus_participant/io.lotus.decorations from the host, then remount.
Suggested fix
give the lotus actions a non-lazy path — in widget.ts, if lazyActions.emit returns false for a lotus action, reply (or error-reply) immediately instead of backlogging; optionally cap/expire the backlog per action type.
Filed from the September 2026 audit (branch lotus).
**Severity:** medium · **Type:** bug · **Confidence:** medium
**Location:** `src/LazyEventEmitter.ts:20-50`, `src/widget.ts:108-113`, `src/lotus/lotusFocus.ts:41`, `src/lotus/lotusDecorations.ts:88`
### Problem
`widget.ts` calls `ev.preventDefault()` for every lotus action and forwards it to `lazyActions`. When a handler has been removed (any of the `start*` teardowns), `super.emit` returns false and the request is appended to `eventBacklogs` forever — never replied to, so the host's `transport.send` pends to its 10 s timeout, and the map grows without bound for the lifetime of the page. On the next registration the whole backlog replays synchronously: each handler calls `w.api.transport.reply(ev.detail, {})` for long-expired request ids (the host logs "response to unknown request" for each), and for `focus_participant` every queued pin is applied in sequence so the *oldest* intent can end up overwritten by whatever arrived last, silently. Nothing ever prunes the backlog.
### How to trigger
unmount the call view (handlers off), send several `io.lotus.focus_participant`/`io.lotus.decorations` from the host, then remount.
### Suggested fix
give the lotus actions a non-lazy path — in `widget.ts`, if `lazyActions.emit` returns false for a lotus action, reply (or error-reply) immediately instead of backlogging; optionally cap/expire the backlog per action type.
---
_Filed from the September 2026 audit (branch `lotus`)._
jared
added this to the EC fork audit 2026-09 · Medium & Low milestone 2026-09-12 02:13:06 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: medium · Type: bug · Confidence: medium
Location:
src/LazyEventEmitter.ts:20-50,src/widget.ts:108-113,src/lotus/lotusFocus.ts:41,src/lotus/lotusDecorations.ts:88Problem
widget.tscallsev.preventDefault()for every lotus action and forwards it tolazyActions. When a handler has been removed (any of thestart*teardowns),super.emitreturns false and the request is appended toeventBacklogsforever — never replied to, so the host'stransport.sendpends to its 10 s timeout, and the map grows without bound for the lifetime of the page. On the next registration the whole backlog replays synchronously: each handler callsw.api.transport.reply(ev.detail, {})for long-expired request ids (the host logs "response to unknown request" for each), and forfocus_participantevery queued pin is applied in sequence so the oldest intent can end up overwritten by whatever arrived last, silently. Nothing ever prunes the backlog.How to trigger
unmount the call view (handlers off), send several
io.lotus.focus_participant/io.lotus.decorationsfrom the host, then remount.Suggested fix
give the lotus actions a non-lazy path — in
widget.ts, iflazyActions.emitreturns false for a lotus action, reply (or error-reply) immediately instead of backlogging; optionally cap/expire the backlog per action type.Filed from the September 2026 audit (branch
lotus).