koala73/worldmonitor · error · ConvexError
API_ACCESS_REQUIRED
API_ACCESS_REQUIRED
Error message
API_ACCESS_REQUIRED
What it means
Entitlement gate on API-key creation: the user has no entitlement row, their entitlement has expired (validUntil in the past), or the plan's feature catalog does not include apiAccess (Pro tier 1 lacks it; API_STARTER tier 2+ has it). The mutation refuses to issue a key because API access is a paid, catalog-driven feature.
Solutions
- Upgrade to an API-enabled plan (API_STARTER or higher) before creating keys
- Check the entitlements row (existence, validUntil, features.apiAccess) to see which gate failed
- If the entitlement looks wrong, verify the plan catalog's apiAccess flags and re-sync the entitlement
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at convex/apiKeys.ts:64 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/8a953f9ce32cbb99.
Report an issue: GitHub.
Appendix: source
Thrown at convex/apiKeys.ts:64
keyHash: v.string(),
scopes: v.optional(v.array(v.string())),
},
handler: async (ctx, args) => {
const userId = await requireUserId(ctx);
// Entitlement gate: only users with apiAccess may create API keys.
// This is catalog-driven — Pro (tier 1) has apiAccess=false;
// API_STARTER+ (tier 2+) have apiAccess=true.
const entitlement = await ctx.db
.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");
}View on GitHub (pinned to eeab0a219f)