You can't vote on a poll you just created (until a reload): the vote targets the poll's local-echo id and throws #284

Open
opened 2026-10-04 19:50:21 -04:00 by jared · 0 comments
Owner

What happens

  1. Create a poll in any room and wait (10 s is plenty).
  2. Click an answer.
  3. Nothing is recorded. The console shows an uncaught Error: Cannot call getPendingEvents with pendingEventOrdering == chronological, and no m.poll.response reaches the server.
  4. After a reload, voting works.

Others' votes do show up for the creator; "End poll" works too.

Why

  • PollContent reads const eventId = mEvent.getId() during render (src/app/components/message/content/PollContent.tsx:102). It is never re-rendered when the SDK swaps the local echo's ~!room:txn id for the real $event id, so the timeline row keeps the local id. Even data-message-id on your own new messages stays ~….
  • handleVote then sends m.relates_to.event_id: '~!…'. The SDK's sendCompleteEvent sees a relation to a pending event, calls room.getPendingEvents(), and that throws under Lotus's chronological ordering. The throw is synchronous, so .catch() doesn't catch it.

Introduced by 0b061584 (2026-07-09, poll lifecycle).

Fix

  • Read the id at click time (mEvent.getId() inside handleVote, or subscribe to MatrixEventEvent.LocalEventIdReplaced).
  • Disable voting while the poll is still pending (mEvent.status !== null).
  • Wrap the send in try/catch.

Worth auditing other handlers that capture mEvent.getId() at render for your own fresh messages. In this audit, reactions, reply-in-thread, pin, delete and edit all worked.

Acceptance criteria: a Playwright test that creates a poll, votes without reloading, and asserts an m.poll.response relating to the real $ id.


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.

**What happens** 1. Create a poll in any room and wait (10 s is plenty). 2. Click an answer. 3. Nothing is recorded. The console shows an uncaught `Error: Cannot call getPendingEvents with pendingEventOrdering == chronological`, and no `m.poll.response` reaches the server. 4. After a reload, voting works. Others' votes do show up for the creator; "End poll" works too. **Why** - `PollContent` reads `const eventId = mEvent.getId()` during render (src/app/components/message/content/PollContent.tsx:102). It is never re-rendered when the SDK swaps the local echo's `~!room:txn` id for the real `$event` id, so the timeline row keeps the local id. Even `data-message-id` on your own new messages stays `~…`. - `handleVote` then sends `m.relates_to.event_id: '~!…'`. The SDK's `sendCompleteEvent` sees a relation to a pending event, calls `room.getPendingEvents()`, and that throws under Lotus's `chronological` ordering. The throw is synchronous, so `.catch()` doesn't catch it. **Introduced by** 0b061584 (2026-07-09, poll lifecycle). **Fix** - Read the id at click time (`mEvent.getId()` inside `handleVote`, or subscribe to `MatrixEventEvent.LocalEventIdReplaced`). - Disable voting while the poll is still pending (`mEvent.status !== null`). - Wrap the send in try/catch. Worth auditing other handlers that capture `mEvent.getId()` at render for your own fresh messages. In this audit, reactions, reply-in-thread, pin, delete and edit all worked. **Acceptance criteria:** a Playwright test that creates a poll, votes without reloading, and asserts an `m.poll.response` relating to the real `$` id. --- _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: mediumarea: messaging labels 2026-10-04 19:50:21 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#284