koala73/worldmonitor · error · ConvexError
INVITEE_EMAIL_UNAVAILABLE
INVITEE_EMAIL_UNAVAILABLE
Error message
INVITEE_EMAIL_UNAVAILABLE
What it means
Structured ConvexError thrown by acceptBusinessInvite when the accepting user's Clerk identity exposes no email address. Invite redemption matches the signed-in email against the invited address, so a user without a resolvable email cannot be matched to a grant.
Solutions
- Ensure the invitee's Clerk profile has a primary email set
- Re-authenticate so a fresh identity token with email claims is available
- Sign in with the account whose address was invited
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at convex/payments/businessSeats.ts:460 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/73d7624fd04ef4d2.
Report an issue: GitHub.
Appendix: source
Thrown at convex/payments/businessSeats.ts:456
return { ok: true as const, status: "revoked" as const };
},
});
/**
* Invitee redeems an HMAC token to accept a Business Pro seat invite. Verifies
* the token, matches the signed-in Clerk email against the invited address,
* checks the underlying Business subscription is still covering, flips the
* grant to `accepted`, stamps `inviteeUserId`, and recomputes the invitee's
* entitlement. Single-use: accepted/revoked/expired tokens are rejected.
*/
export const acceptBusinessInvite = mutation({
args: { grantId: v.id("businessProGrants"), token: v.string() },
handler: async (ctx, args) => {
const userId = await requireUserId(ctx);
const identity = await resolveUserIdentity(ctx);
const inviteeEmail = identity?.email?.trim().toLowerCase();
if (!inviteeEmail) {
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" });View on GitHub (pinned to 7d06c8633d)