{"record":{"id":"451266076803d943","repo":"koala73/worldmonitor","slug":"business-not-active","errorCode":"BUSINESS_NOT_ACTIVE","errorMessage":"BUSINESS_NOT_ACTIVE","messagePattern":"BUSINESS_NOT_ACTIVE","errorType":"exception","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/payments/businessSeats.ts","lineNumber":494,"sourceCode":"    }\n    if (grant.inviteeEmail !== inviteeEmail) {\n      throw new ConvexError({ kind: \"INVITE_EMAIL_MISMATCH\" });\n    }\n    if (!sameDomain(grant.inviteeEmail, inviteeEmail)) {\n      throw new ConvexError({ kind: \"INVITE_EMAIL_MISMATCH\" });\n    }\n    if (!isCorporateDomain(inviteeEmail)) {\n      throw new ConvexError({ kind: \"INVITEE_DOMAIN_NOT_CORPORATE\" });\n    }\n\n    const businessSub = await ctx.db\n      .query(\"subscriptions\")\n      .withIndex(\"by_dodoSubscriptionId\", (q) =>\n        q.eq(\"dodoSubscriptionId\", grant.businessSubscriptionId),\n      )\n      .unique();\n    if (!businessSub || businessSub.planKey !== \"api_business\" || !isCoveringAt(businessSub, now)) {\n      throw new ConvexError({ kind: \"BUSINESS_NOT_ACTIVE\" });\n    }\n\n    await ctx.db.patch(args.grantId, {\n      status: \"accepted\",\n      inviteeUserId: userId,\n      acceptedAt: now,\n    });\n\n    await ctx.runMutation(\n      internal.payments.subscriptionHelpers.recomputeEntitlementForUser,\n      { userId, eventTimestamp: now },\n    );\n\n    return { ok: true as const };\n  },\n});\n","sourceCodeStart":476,"sourceCodeEnd":511,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/payments/businessSeats.ts#L476-L511","documentation":"At accept time, `acceptBusinessInvite` looks up the Business subscription by `grant.businessSubscriptionId` and requires it to (a) exist, (b) have `planKey === \"api_business\"`, and (c) be covering at the current time via `isCoveringAt`. If any fails — typically because the owner cancelled, lapsed, or downgraded between sending the invite and the accept — the invite cannot be honored.","triggerScenarios":"The owner's Business subscription is no longer active/covering when the invitee clicks accept: cancelled, payment failed past grace, or the planKey changed away from `api_business`.","commonSituations":"Owner cancelled the Business plan after sending invites; a payment failure ended coverage; subscription was downgraded to an individual plan; grant outlived the subscription that issued it.","solutions":["Have the owner reactivate or repurchase the `api_business` subscription, then accept again (a fresh grant may be needed if the grant was revoked on lapse)","If the owner believes the subscription is active, verify the `subscriptions` row's covering window and planKey","Note: grants are auto-revoked when the Business subscription stops covering, so BUSINESS_NOT_ACTIVE often co-occurs with a grant already cleaned up"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await convex.mutation(api.payments.businessSeats.acceptBusinessInvite, { grantId, token });\n} catch (err) {\n  if (err.data?.kind === 'BUSINESS_NOT_ACTIVE') {\n    // inform user the owner's Business plan is no longer active; suggest contacting owner\n  } else { throw err; }\n}","preventionTips":["Accept invites promptly while the Business subscription is covering","If BUSINESS_NOT_ACTIVE appears, expect the grant may already be auto-revoked on lapse"],"tags":["payments","business-seats","subscription","lifecycle"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}