koala73/worldmonitor · error · ConvexError

CANNOT_INVITE_SELF

CANNOT_INVITE_SELF

Error message

CANNOT_INVITE_SELF

What it means

Thrown by `inviteSeats` during the per-email loop when an invitee address equals the owner's own (normalized) email. Owners cannot invite themselves as a seat. Object-typed ConvexError (`{ kind: "CANNOT_INVITE_SELF" }`).

Source

Thrown at convex/payments/businessSeats.ts:182

        q.eq("businessSubscriptionId", businessSub.dodoSubscriptionId),
      )
      .collect();
    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" });
      }
      if (!sameDomain(ownerEmail, email)) {
        throw new ConvexError({ kind: "INVITEE_DOMAIN_MISMATCH" });
      }

      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;
        }

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Remove the owner's own email from the invite list before submitting.
  2. Client-side, filter out the current user's email from the recipients.
  3. Warn if the owner's address is detected in the input.

Example fix

// before
await inviteSeats({ emails: ["owner@acme.com", "a@acme.com"] });

// after
const emails = recipients.filter(e => e !== currentUserEmail);
await inviteSeats({ emails });
Defensive patterns

Strategy: validation

Validate before calling

const emails = rawEmails.filter(e => e.trim().toLowerCase() !== ownerEmail.toLowerCase());
if (emails.length === 0) { showToast("You can't invite yourself."); return; }
await inviteSeats({ emails });

Prevention

When it happens

Trigger: The owner's email appears in the `emails` array passed to `inviteSeats` (e.g. `inviteSeats({ emails: ["owner@acme.com"] })` where owner@acme.com is the caller).

Common situations: Owner pasted their own address by mistake; an autocomplete defaulted to the signed-in user; 'invite all' from a contact list that included the owner.

Related errors


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/7c3bc5679d41cc13. Report an issue: GitHub.