koala73/worldmonitor · error · ConvexError

INVITE_EMAIL_MISMATCH

INVITE_EMAIL_MISMATCH

Error message

INVITE_EMAIL_MISMATCH

What it means

Convex policy error in the business-seat invite acceptance mutation: the authenticated user's email (resolved from identity, lowercased) does not match grant.inviteeEmail. The invite was issued to a different recipient; this prevents claiming a seat intended for someone else.

Solutions

  1. Accept the invite while signed in with the exact invited email account
  2. Ask the seat owner to reissue the invite to the email you actually use
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at convex/payments/businessSeats.ts:478 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-08-21). Data as JSON: /api/errors/4e569a45fe4ceb3e. Report an issue: GitHub.

Appendix: source

Thrown at convex/payments/businessSeats.ts:474

      throw new ConvexError({ kind: "INVITEE_EMAIL_UNAVAILABLE" });
    }

    const grant = await ctx.db.get(args.grantId);
    if (!grant) {
      throw new ConvexError({ kind: "GRANT_NOT_FOUND" });
    }
    if (grant.status !== "pending") {
      throw new ConvexError({ kind: "INVITE_ALREADY_USED" });
    }
    const now = Date.now();
    if (grant.expiresAt <= now) {
      throw new ConvexError({ kind: "INVITE_EXPIRED" });
    }
    if (!(await verifyBusinessInviteToken(args.grantId, args.token))) {
      throw new ConvexError({ kind: "INVALID_INVITE_TOKEN" });
    }
    if (grant.inviteeEmail !== inviteeEmail) {
      throw new ConvexError({ kind: "INVITE_EMAIL_MISMATCH" });
    }
    if (!sameDomain(grant.inviteeEmail, inviteeEmail)) {
      throw new ConvexError({ kind: "INVITE_EMAIL_MISMATCH" });
    }
    if (!isCorporateDomain(inviteeEmail)) {
      throw new ConvexError({ kind: "INVITEE_DOMAIN_NOT_CORPORATE" });
    }

    const businessSub = await ctx.db
      .query("subscriptions")
      .withIndex("by_dodoSubscriptionId", (q) =>
        q.eq("dodoSubscriptionId", grant.businessSubscriptionId),
      )
      .unique();
    if (!businessSub || !isBusinessPlan(businessSub.planKey) || !isCoveringAt(businessSub, now)) {
      throw new ConvexError({ kind: "BUSINESS_NOT_ACTIVE" });
    }

View on GitHub (pinned to 7d06c8633d)