koala73/worldmonitor · error · ConvexError
COMPANY_MONITORING_ACCESS_DENIED
COMPANY_MONITORING_ACCESS_DENIED
Error message
COMPANY_MONITORING_ACCESS_DENIED
What it means
Thrown by createApiKey when the caller requests Company Monitoring scopes but ensureActiveAccount() returns null — meaning the root Company Monitoring account could not be provisioned or activated for this user/entitlement. Scopes were requested (non-empty), so the key cannot be issued without a backing account; requesting no scopes skips this entirely. This guards scoped keys against being minted with no usable backing account.
Source
Thrown at convex/apiKeys.ts:74
.query("entitlements")
.withIndex("by_userId", (q) => q.eq("userId", userId))
.first();
if (
!entitlement ||
entitlement.validUntil < Date.now() ||
!entitlement.features.apiAccess
) {
throw new ConvexError("API_ACCESS_REQUIRED");
}
const scopes = normalizeCompanyMonitoringScopes(args.scopes);
// Issuing a scoped key is a first-use entry point, so it provisions the
// root. Requesting no scopes must stay entirely off Company Monitoring.
const companyMonitoringAccount = scopes
? await ensureActiveAccount(ctx, userId, entitlement)
: null;
if (scopes && !companyMonitoringAccount) {
throw new ConvexError("COMPANY_MONITORING_ACCESS_DENIED");
}
if (!args.name.trim()) {
throw new ConvexError("INVALID_NAME");
}
if (!/^wm_[a-f0-9]{5}$/.test(args.keyPrefix)) {
throw new ConvexError("INVALID_PREFIX");
}
if (!/^[a-f0-9]{64}$/.test(args.keyHash)) {
throw new ConvexError("INVALID_HASH");
}
// Enforce per-user key limit (count only non-revoked keys).
//
// API keys intentionally reject at the cap instead of silently rotating a
// valid key. If a prior race left too many active rows, converge by
// revoking enough oldest overflow rows to make room for this create.
const existing = await ctx.dbView on GitHub (pinned to ffec79ac33)
Solutions
- Verify Company Monitoring is enabled for the user's plan tier in the entitlement catalog before requesting scopes.
- If scopes are not needed for this key, call createApiKey without the scopes argument (or with an empty array) to skip Company Monitoring entirely.
- Inspect ensureActiveAccount in convex/companyMonitoring/accounts.ts to learn why it returned null for this entitlement, and resolve the upstream blocker.
- Contact the operator to confirm the account's Company Monitoring eligibility is active and not suspended.
Example fix
// before
await createApiKey(ctx, {
name, keyPrefix, keyHash,
scopes: ["company-monitoring:read"], // triggers ensureActiveAccount
});
// after — omit scopes when CM is not provisioned
await createApiKey(ctx, { name, keyPrefix, keyHash }); Defensive patterns
Strategy: validation
Validate before calling
// Before createApiKey, confirm CM eligibility or omit scopes
const entitlement = await ctx.db.query("entitlements")
.withIndex("by_userId", q => q.eq("userId", userId)).first();
const cmEnabled = entitlement?.features?.companyMonitoring === true;
const scopes = cmEnabled ? requestedScopes : undefined;
await createApiKey(ctx, { name, keyPrefix, keyHash, scopes }); Type guard
function hasCompanyMonitoring(ent: unknown): ent is { features: { companyMonitoring: true } } {
return !!ent && typeof ent === "object"
&& !!((ent as any).features)?.companyMonitoring;
} Try / catch
try {
await createApiKey(ctx, { name, keyPrefix, keyHash, scopes });
} catch (e) {
if (e instanceof ConvexError && e.message === "COMPANY_MONITORING_ACCESS_DENIED") {
// retry without scopes, or surface CM-not-available to the user
await createApiKey(ctx, { name, keyPrefix, keyHash });
} else throw e;
} Prevention
- Gate the scopes UI option on a Company Monitoring eligibility check before exposing it.
- Default scopes to undefined when CM status is unknown.
- Surface CM provisioning errors to the user with a 'contact support' path rather than retrying blindly.
When it happens
Trigger: Calling convex/apiKeys:createApiKey with a non-empty scopes array where ensureActiveAccount(ctx, userId, entitlement) resolves to null — e.g. the entitlement is valid for API access but the Company Monitoring provisioning path (accounts.ts:ensureActiveAccount) declined to create/activate the root account (disabled feature, quota, or internal guard returned null).
Common situations: The user's plan tier allows apiAccess but Company Monitoring is not enabled or has been suspended for the account; an entitlement catalog change removed Company Monitoring eligibility mid-cycle; a stale/inconsistent entitlement row passes the apiAccess gate but fails the downstream provisioning check.
Related errors
- API_ACCESS_REQUIRED
- PRO_REQUIRED
- COMPANY_MONITORING_ACCESS_DENIED
- COMPANY_MONITORING_ACCESS_DENIED
- COMPANY_MONITORING_ACCOUNT_INACTIVE
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/c49da4e0cc38a61d.
Report an issue: GitHub.