{"record":{"id":"2eb1bc88ffa82076","repo":"koala73/worldmonitor","slug":"too-many-emails","errorCode":"TOO_MANY_EMAILS","errorMessage":"TOO_MANY_EMAILS","messagePattern":"TOO_MANY_EMAILS","errorType":"error_code","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/payments/businessSeats.ts","lineNumber":156,"sourceCode":"\n    // Serialize concurrent inviteSeats / removeSeat calls for this Business\n    // subscription so the cap check below cannot be bypassed by a race.\n    await touchBusinessSeatLock(ctx, businessSub.dodoSubscriptionId, now);\n\n    const normalizedOwnerEmail = ownerEmail.toLowerCase();\n    // Dedupe: the existingByEmail check below only guards against emails\n    // that already had a grant BEFORE this call — a duplicate within the\n    // SAME args.emails array would otherwise slip past it (neither\n    // occurrence is in existingGrants yet) and create two grant rows for\n    // one invitee.\n    const emails = Array.from(\n      new Set(args.emails.map((e) => e.trim().toLowerCase()).filter((e) => e.length > 0)),\n    );\n    if (emails.length === 0) {\n      throw new ConvexError({ kind: \"NO_EMAILS_PROVIDED\" });\n    }\n    if (emails.length > MAX_SEATS) {\n      throw new ConvexError({ kind: \"TOO_MANY_EMAILS\" });\n    }\n\n    // Check existing grants for duplicates (active or pending).\n    const existingGrants = await ctx.db\n      .query(\"businessProGrants\")\n      .withIndex(\"by_businessSubscriptionId\", (q) =>\n        q.eq(\"businessSubscriptionId\", businessSub.dodoSubscriptionId),\n      )\n      .collect();\n    const existingByEmail = new Map(\n      existingGrants.map((g) => [g.inviteeEmail, g]),\n    );\n\n    // Separate new invites from duplicates before the cap check so a duplicate\n    // re-invite is idempotent even when the cap is full.\n    const newEmails: string[] = [];\n    const results: Array<{\n      email: string;","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/convex/payments/businessSeats.ts#L138-L174","documentation":"Structured ConvexError thrown by inviteSeats when the number of invite emails exceeds the per-call maximum. The mutation caps how many addresses may be submitted at once; larger batches are rejected before creating any grants.","triggerScenarios":"Thrown at convex/payments/businessSeats.ts:157 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Split the invite list into smaller batches within the per-call limit","Trim invalid or already-invited entries client-side before submitting"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}