Offline outbox: two open tabs overwrite each other's saved queue #268

Closed
opened 2026-10-04 17:41:28 -04:00 by jared · 0 comments
Owner

OutboxFeature loads lotus_outbox_v1 once at mount and then saves its own in-memory entries array over the whole key on every change (src/app/features/outbox/OutboxFeature.tsx:86-101, src/app/utils/outbox.ts:166-173).

With two tabs open:

  1. Tab A queues an unsent message (saved).
  2. Tab B's next local echo saves B's list, without A's entry.
  3. If A is then closed or crashes, its unsent message is no longer in the outbox and won't come back after a reload.

Fix: re-read and merge before saving (read → withEntry/withoutEntry → write), and/or listen to the storage event.

Low priority: it needs two tabs plus a crash while offline.

Related: #112.

More context

Introduced by 02d86cae (2026-09-28, #112).

Repro sketch

  1. Two tabs open, browser offline.
  2. Send "A" in tab 1.
  3. Send "B" in tab 2. Tab 2's save writes [B] over [A].
  4. Close tab 1 and go back online.
  5. "A" is never retried and no longer shows as failed after a reload.

Fix: read-modify-write (re-loadOutbox inside persist) and/or a storage event listener that merges.


Found in the October 2026 client audit (Playwright against a local Synapse + LiveKit stack, plus code reading). Code refs are at cinny 30e2dbcd, cinny-desktop 8f67ec7, element-call 3b0050e8.

`OutboxFeature` loads `lotus_outbox_v1` once at mount and then saves its own in-memory `entries` array over the whole key on every change (src/app/features/outbox/OutboxFeature.tsx:86-101, src/app/utils/outbox.ts:166-173). With two tabs open: 1. Tab A queues an unsent message (saved). 2. Tab B's next local echo saves B's list, **without** A's entry. 3. If A is then closed or crashes, its unsent message is no longer in the outbox and won't come back after a reload. **Fix:** re-read and merge before saving (read → `withEntry`/`withoutEntry` → write), and/or listen to the `storage` event. Low priority: it needs two tabs plus a crash while offline. Related: #112. ### More context **Introduced by** 02d86cae (2026-09-28, #112). **Repro sketch** 1. Two tabs open, browser offline. 2. Send "A" in tab 1. 3. Send "B" in tab 2. Tab 2's save writes `[B]` over `[A]`. 4. Close tab 1 and go back online. 5. "A" is never retried and no longer shows as failed after a reload. **Fix:** read-modify-write (re-`loadOutbox` inside `persist`) and/or a `storage` event listener that merges. --- _Found in the October 2026 client audit (Playwright against a local Synapse + LiveKit stack, plus code reading). Code refs are at cinny `30e2dbcd`, cinny-desktop `8f67ec7`, element-call `3b0050e8`._
jared added the bugpriority: lowarea: messaging labels 2026-10-04 17:41:28 -04:00
jared closed this issue 2026-10-19 15:31:01 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#268