koala73/worldmonitor · error · ConvexError
COMPANY_LIMIT_REACHED
COMPANY_LIMIT_REACHED
Error message
COMPANY_LIMIT_REACHED
What it means
Thrown by createCompanyForOwner (convex/companyMonitoring/companies.ts:137) when the account's companyCount has reached its limit — account.companyLimit if set, otherwise the default COMPANY_LIMIT. Adding another monitored company is refused before any insert happens.
Solutions
- Surface an upgrade-path message to the user (raise account.companyLimit) or have them remove companies to free slots
- Check the count before submitting: query the account's companyCount/companyLimit and block the UI action early
- For imports, pre-compute how many slots remain and stop or chunk the batch accordingly
- If count looks wrong, verify that removals actually decrement companyCount (the patch does companyCount + 1 on insert, so the inverse must run on removal)
Example fix
// before
await api.companyMonitoring.createCompanyForOwner({ ownerUserId, clientRequestId, company });
// after
const account = await api.companyMonitoring.getAccount({ ownerUserId });
const limit = account.companyLimit ?? DEFAULT_COMPANY_LIMIT;
if ((account.companyCount ?? 0) >= limit) {
throw new UserFacingError(`Company limit reached (${limit}). Remove a company or upgrade.`);
}
await api.companyMonitoring.createCompanyForOwner({ ownerUserId, clientRequestId, company }); Defensive patterns
Strategy: validation
Validate before calling
const account = await api.companyMonitoring.getAccount({ ownerUserId });
const limit = account.companyLimit ?? DEFAULT_COMPANY_LIMIT;
const count = account.companyCount ?? 0;
if (count >= limit) {
throw new UserFacingError(`Company limit reached (${count}/${limit}). Remove a company or upgrade the plan.`);
}
await api.companyMonitoring.createCompanyForOwner({ ownerUserId, clientRequestId, company }); Try / catch
try {
await api.companyMonitoring.createCompanyForOwner({ ownerUserId, clientRequestId, company });
} catch (err) {
if (err instanceof ConvexError && err.data === "COMPANY_LIMIT_REACHED") {
ui.showUpgradePrompt(); // or guide the user to remove companies
return;
}
throw err;
} Prevention
- Show remaining slots (companyCount/companyLimit) in the UI before the add action
- Chunk imports to the remaining slots and report the shortfall instead of failing mid-batch
- After plan changes, reconcile companyLimit with companyCount before the next add
- Verify removals actually decrement companyCount so slots are freed
When it happens
Trigger: Calling add-company after the account already tracks the maximum number of companies; companyLimit was lowered (plan change) below the current companyCount, so even the next add fails; concurrent adds racing the companyCount check when the limit is nearly reached.
Common situations: Plan downgrade shrinking limits; bulk imports hitting the ceiling mid-batch; default limit surprise in staging with COMPANY_LIMIT lower than production expectations; count drift after failed deletes.
Related errors
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-08-21).
Data as JSON: /api/errors/96d90b6330884cb4.
Report an issue: GitHub.
Appendix: source
Thrown at convex/companyMonitoring/companies.ts:137
)
.unique();
if (replay) {
if (replay.directFingerprint !== directFingerprint) {
throw new ConvexError("REPLAY_CONFLICT");
}
return { status: "replayed", companyId: replay.companyId };
}
const noop = await findNoopByCustomerReference(
ctx,
account.logicalAccountId,
company.customerReference,
);
if (noop) return { status: "noop", companyId: noop.companyId };
const companyCount = account.companyCount ?? 0;
if (companyCount >= (account.companyLimit ?? COMPANY_LIMIT)) {
throw new ConvexError("COMPANY_LIMIT_REACHED");
}
const companyId = await insertNormalizedCompany(ctx, account, company, {
directRequestId: clientRequestId,
directFingerprint,
});
await scheduleCompanySourcesHandler(ctx, {
ownerAccountId: account.logicalAccountId,
companyId,
});
await ctx.db.patch(account._id, {
companyCount: companyCount + 1,
snapshotGeneration: (account.snapshotGeneration ?? 0) + 1,
updatedAt: Date.now(),
});
return { status: "created", companyId };
},
});View on GitHub (pinned to 7d06c8633d)