fix(invites): distinguish CAS contention from a real use-cap (retry vs reject)

claimInviteUse returned null both when the cap was truly reached and when it
lost the compare-and-swap 5× under load, so a legitimate joiner on a busy
high-cap invite got a false 'invite has reached its use cap'. Return a
discriminated result; map contention to a distinct 'try again' message.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Moheeb Zara
2026-08-04 19:10:52 -07:00
co-authored by Claude Opus 4.8
parent 33b2475e67
commit 61ffc2e01f
3 changed files with 77 additions and 17 deletions
+10 -2
View File
@@ -182,8 +182,16 @@ export async function joinSpace(formData: {
// (The pre-check above is a fast UX reject; this is the authoritative guard.)
let claimedCount: number | null = null
if (invite) {
claimedCount = await claimInviteUse(admin, invite.id, invite.uses_count, invite.max_uses)
if (claimedCount === null) return { error: 'This invite has reached its use cap.' }
const claim = await claimInviteUse(admin, invite.id, invite.uses_count, invite.max_uses)
if (!claim.ok) {
return {
error:
claim.reason === 'contention'
? 'Too many people are joining at once — please try again.'
: 'This invite has reached its use cap.',
}
}
claimedCount = claim.count
}
const { data: newMember, error: memberErr } = await admin