koala73/worldmonitor · error · ConvexError

INVITEE_DOMAIN_NOT_CORPORATE

INVITEE_DOMAIN_NOT_CORPORATE

Error message

INVITEE_DOMAIN_NOT_CORPORATE

What it means

Thrown by convex/payments/businessSeats.ts when a business seat invitation is rejected because the invitee's email domain does not qualify as a corporate domain. Business seat plans require invitees to belong to an approved corporate email domain (free/public providers like gmail.com, yahoo.com are rejected). The check fails before the invitation is created or billed, so no seat charge occurs.

Solutions

  1. Ask the invitee to use their corporate email address associated with the purchasing organization.
  2. Have an admin add the invitee's domain to the organization's approved corporate domain list, then retry the invitation.
  3. If the invitee genuinely cannot use a corporate email, transfer or assign the seat directly to an existing verified user instead of inviting by email.
  4. Verify the organization's domain settings were not recently changed and re-run the invitation after correcting them.
Defensive patterns

Strategy: try-catch

When it happens

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

Common situations: See trigger scenarios.


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-01). Data as JSON: /api/errors/015352ef981c16b1. Report an issue: GitHub.

Appendix: source

Thrown at convex/payments/businessSeats.ts:184

    const existingByEmail = new Map(
      existingGrants.map((g) => [g.inviteeEmail, g]),
    );

    // Separate new invites from duplicates before the cap check so a duplicate
    // re-invite is idempotent even when the cap is full.
    const newEmails: string[] = [];
    const results: Array<{
      email: string;
      grantId: string;
      status: "created" | "already_pending" | "already_accepted";
    }> = [];

    for (const email of emails) {
      if (email === normalizedOwnerEmail) {
        throw new ConvexError({ kind: "CANNOT_INVITE_SELF" });
      }
      if (!isCorporateDomain(email)) {
        throw new ConvexError({ kind: "INVITEE_DOMAIN_NOT_CORPORATE" });
      }

      const existing = existingByEmail.get(email);
      if (existing) {
        if (existing.status === "accepted") {
          results.push({ email, grantId: existing._id, status: "already_accepted" });
          continue;
        }
        if (existing.status === "pending" && existing.expiresAt > now) {
          results.push({ email, grantId: existing._id, status: "already_pending" });
          continue;
        }
      }
      newEmails.push(email);
    }

    const currentCount = await countActiveOrPendingGrants(
      ctx,

View on GitHub (pinned to 7d06c8633d)