#9238·formbricks

Invite rate limit charges failed invites against the daily budget

Author: NovohudonossorCreated Sep 10, 2026Updated Sep 12, 2026

Issue Summary

Since #9182 the invite budget is charged before the invite is created, so recipients whose invite fails still use up the organization's daily budget. Before #9182 the single-invite path recorded usage only when an invite was actually created.

apps/web/modules/organization/settings/teams/actions.ts on main (e68a469a):

  • inviteUserAction calls applyInviteRateLimit(parsedInput.organizationId) at line 342, before inviteUser(...) at line 344.
  • bulkInviteUsersAction calls applyInviteRateLimit(organizationId, invitees.length) at line 442 for the whole batch, before the per-recipient loop. Recipients that then fail — user_already_member, invite_already_exists, duplicate_team_ids, invalid_team_ids from lib/invite-failure.ts — are only reported back as success: false (lines 467 and 470).
  • The onboarding invite action does the same (apps/web/modules/setup/organization/[organizationId]/invite/actions.ts:36).

Before #9182 (313470bf, actions.ts:366):

typescript
if (inviteId) {
  await recordRateLimitUsage(rateLimitConfigs.actions.inviteMember, parsedInput.organizationId);

checkRateLimit now takes the units with an atomic INCRBY, and nothing in rate-limit.ts or helpers.ts gives them back.

Example on a self-hosted instance with the default INVITE_RATE_LIMIT_PER_24_HOURS=50: a bulk invite of 40 addresses where 15 people are already members creates 25 invites, charges 40, and leaves 10 for the rest of the day.

Expected Behavior

Only recipients that actually got an invite count against the budget, as before #9182 — for example by refunding the failed count after the bulk loop, or by recording usage per created invite while keeping the up-front check that refuses oversized requests.

Other information (incl. screenshots, Formbricks version, steps to reproduce,...)

The comment on the Lua script ("Refusing before INCRBY prevents an oversized request from consuming the remaining budget") is about oversized requests, not failed invites, and the PR description does not mention failures either, so I don't think charging them was intended. Close this if it is the anti-abuse trade-off you want.

Your Environment

Formbricks main at e68a469a (the merge of #9182). Read from source; I have not reproduced it on a running instance.


To be upfront about where this came from: I found it with a code review tool I am building (ReviewGate) while running it over real repositories, then compared the code before and after #9182 by hand before writing this up.