koala73/worldmonitor · error · ConvexError
OWNER_DOMAIN_NOT_CORPORATE
OWNER_DOMAIN_NOT_CORPORATE
Error message
OWNER_DOMAIN_NOT_CORPORATE
What it means
Structured ConvexError thrown by inviteSeats when the owner's email domain is missing or classified as non-corporate (free providers like gmail.com). Business seats may only be granted within a corporate domain, so a consumer-email owner is blocked from inviting.
Solutions
- Sign in with (or switch the account email to) a corporate domain address
- Verify the domain passes the isCorporateDomain check and is not on the free-provider list
- Contact support if a legitimate corporate domain is misclassified
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at convex/payments/businessSeats.ts:137 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/834aae772b7c57f5.
Report an issue: GitHub.
Appendix: source
Thrown at convex/payments/businessSeats.ts:136
export const inviteSeats = mutation({
args: { emails: v.array(v.string()) },
handler: async (ctx, args) => {
const userId = await requireUserId(ctx);
const identity = await resolveUserIdentity(ctx);
const ownerEmail = identity?.email?.trim();
if (!ownerEmail) {
throw new ConvexError({ kind: "OWNER_EMAIL_UNAVAILABLE" });
}
const now = Date.now();
const businessSub = await getCoveringBusinessSubscription(ctx, userId, now);
if (!businessSub) {
throw new ConvexError({ kind: "OWNER_NOT_BUSINESS" });
}
const ownerDomain = extractDomain(ownerEmail);
if (!ownerDomain || !isCorporateDomain(ownerEmail)) {
throw new ConvexError({ kind: "OWNER_DOMAIN_NOT_CORPORATE" });
}
// Serialize concurrent inviteSeats / removeSeat calls for this Business
// subscription so the cap check below cannot be bypassed by a race.
await touchBusinessSeatLock(ctx, businessSub.dodoSubscriptionId, now);
const normalizedOwnerEmail = ownerEmail.toLowerCase();
// Dedupe: the existingByEmail check below only guards against emails
// that already had a grant BEFORE this call — a duplicate within the
// SAME args.emails array would otherwise slip past it (neither
// occurrence is in existingGrants yet) and create two grant rows for
// one invitee.
const emails = Array.from(
new Set(args.emails.map((e) => e.trim().toLowerCase()).filter((e) => e.length > 0)),
);
if (emails.length === 0) {
throw new ConvexError({ kind: "NO_EMAILS_PROVIDED" });
}View on GitHub (pinned to 7d06c8633d)