koala73/worldmonitor · error · ConvexError
GRANT_NOT_FOUND
GRANT_NOT_FOUND
Error message
GRANT_NOT_FOUND
What it means
Structured ConvexError thrown by the removeSeat mutation when the supplied businessProGrants id does not exist in the database (unknown or already-deleted grant). The owner-only removal guard cannot proceed without a grant row.
Solutions
- Refresh the seat list — the grant may have already been removed
- Pass the exact grant id returned by the seat listing query
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at convex/payments/businessSeats.ts:421 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/12a355c7516c6a8d.
Report an issue: GitHub.
Appendix: source
Thrown at convex/payments/businessSeats.ts:417
createdAt: g.createdAt,
acceptedAt: g.acceptedAt ?? null,
expiresAt: g.expiresAt,
})),
};
},
});
/**
* Owner-only removal of a single seat. Revokes the grant and recomputes the
* invitee's entitlement.
*/
export const removeSeat = mutation({
args: { grantId: v.id("businessProGrants") },
handler: async (ctx, args) => {
const userId = await requireUserId(ctx);
const grant = await ctx.db.get(args.grantId);
if (!grant) {
throw new ConvexError({ kind: "GRANT_NOT_FOUND" });
}
if (grant.ownerUserId !== userId) {
throw new ConvexError({ kind: "NOT_OWNER" });
}
if (grant.status !== "pending" && grant.status !== "accepted") {
return { ok: true as const, status: "already_inactive" as const };
}
const now = Date.now();
// Serialize with inviteSeats via the per-Business-subscription lock row.
await touchBusinessSeatLock(ctx, grant.businessSubscriptionId, now);
await ctx.db.patch(args.grantId, { status: "revoked" });
if (grant.inviteeUserId) {
await ctx.runMutation(
internal.payments.subscriptionHelpers.recomputeEntitlementForUser,
{ userId: grant.inviteeUserId, eventTimestamp: now },View on GitHub (pinned to 7d06c8633d)