koala73/worldmonitor · error · ConvexError

COMPANY_MONITORING_ACCESS_DENIED

Error message

COMPANY_MONITORING_ACCESS_DENIED

What it means

Thrown by requireActiveAccount (_shared.ts:98-101) when activeAccountForOwner returns null — meaning no active, entitled, non-terminal companyMonitoringAccounts row exists for the given ownerUserId. A ConvexError with this exact message surfaces to the client as a 4xx-style application error. This guards every Company Monitoring entry point that requires a live entitled account.

Source

Thrown at convex/companyMonitoring/_shared.ts:100

    entitlement.planKey !== "free" &&
    entitlement.features.tier > 0 &&
    entitlement.validUntil >= Date.now(),
  );
  if (
    !account ||
    !activeEntitlement ||
    account.lifecycle !== "entitled" ||
    account.ownerUserId !== ownerUserId ||
    account.terminalReason
  ) {
    return null;
  }
  return account;
}

export async function requireActiveAccount(ctx: CompanyMonitoringCtx, ownerUserId: string) {
  const account = await activeAccountForOwner(ctx, ownerUserId);
  if (!account) throw new ConvexError("COMPANY_MONITORING_ACCESS_DENIED");
  return account;
}

type CustomerClaimType =
  | "alias"
  | "domain"
  | "legal_identifier"
  | "x_account_id"
  | "x_handle"
  | "location"
  | "customer_reference";

export function customerClaimAllowedUses(type: CustomerClaimType) {
  if (
    type === "alias" ||
    type === "domain" ||
    type === "legal_identifier" ||
    type === "x_account_id" ||

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Gate the client-side UI on the user's entitlement status before allowing the action — do not call Company Monitoring endpoints for free/lapsed users.
  2. If the user should be entitled, verify their entitlements row exists and validUntil >= now via `npx convex run` or the dashboard.
  3. If the account was never provisioned, trigger syncCompanyMonitoringAccountFromEntitlement (via an entitlement write or a provisioned entry point like requireProvisionedAccount).
  4. If the account was terminalized in error, investigate the terminalReason and lifecycleSequence in the companyMonitoringAccounts row.
Defensive patterns

Strategy: validation

Validate before calling

// Before calling a Company Monitoring endpoint, check entitlement client-side:
const entitled = entitlement && entitlement.planKey !== "free" && entitlement.features.tier > 0 && entitlement.validUntil >= Date.now();
if (!entitled) {
  // show upgrade/paywall UI instead of calling the backend
}

Try / catch

try {
  await ctx.runMutation(internal.companyMonitoring.someEndpoint, args);
} catch (err) {
  if (err instanceof ConvexError && err.message === "COMPANY_MONITORING_ACCESS_DENIED") {
    // surface a paywall/upgrade prompt to the user
    throw new Error("Company Monitoring requires an active PRO subscription.");
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling a Company Monitoring mutation/query for a user who has no entitlement (free plan), whose entitlement expired (validUntil < now), whose account lifecycle is not 'entitled', whose account has a terminalReason set, or whose account ownerUserId does not match. Also when the account simply does not exist yet (never provisioned).

Common situations: A free-plan user attempts to use Company Monitoring features. A user's PRO subscription lapsed and the account transitioned to 'entitlement_lapsed'. The user's account was terminalized (owner_deleted/account_deleted). The account was never provisioned because the entitlement write path hasn't synced yet.

Related errors


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