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
- Ask the invitee to use their corporate email address associated with the purchasing organization.
- Have an admin add the invitee's domain to the organization's approved corporate domain list, then retry the invitation.
- 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.
- 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)