Bug: Treevite invite race condition creates orphaned user/participant records
Summary
When two requests race on the same treevite invite code, the "loser" (the request that loses the UPDATE ... WHERE status = 0 race) returns polis_err_treevite_invite_race_condition — but the user and participant records it created are never cleaned up.
Location
server/src/invites/treevites.ts, lines ~410-431 in handleAcceptInvite:
- Lines 410-419: Create anonymous user + participant (before checking if the invite is still available)
- Lines 423-426:
UPDATE treevite_invites SET status = 1 ... WHERE id = ($2) AND status = 0 RETURNING id
- Lines 428-431: If UPDATE returns no rows → return 400 error, but the user/participant from step 1 remain in the DB
Impact
- Orphaned
users and participants rows accumulate over time
conversations.participant_count is incremented by the pid_auto trigger but never decremented
- Low severity in practice (requires exact timing of concurrent invite acceptance)
Suggested fix
Move user/participant creation (step 1) to after the successful UPDATE (step 2). This way, the loser never creates records that need cleanup.
Discovered while
Investigating the flaky treevite.test.ts test "should handle multiple participants trying to use same invite" — the test expected polis_err_treevite_invalid_or_used_invite but sometimes received polis_err_treevite_invite_race_condition. Test fix in the associated PR.
Bug: Treevite invite race condition creates orphaned user/participant records
Summary
When two requests race on the same treevite invite code, the "loser" (the request that loses the
UPDATE ... WHERE status = 0race) returnspolis_err_treevite_invite_race_condition— but the user and participant records it created are never cleaned up.Location
server/src/invites/treevites.ts, lines ~410-431 inhandleAcceptInvite:UPDATE treevite_invites SET status = 1 ... WHERE id = ($2) AND status = 0 RETURNING idImpact
usersandparticipantsrows accumulate over timeconversations.participant_countis incremented by thepid_autotrigger but never decrementedSuggested fix
Move user/participant creation (step 1) to after the successful UPDATE (step 2). This way, the loser never creates records that need cleanup.
Discovered while
Investigating the flaky
treevite.test.tstest "should handle multiple participants trying to use same invite" — the test expectedpolis_err_treevite_invalid_or_used_invitebut sometimes receivedpolis_err_treevite_invite_race_condition. Test fix in the associated PR.