Dokploy/dokploy · warning · TRPCError
FORBIDDEN
FORBIDDEN
Error message
You've reached your plan's limit of ${limit} ${resourceLabels[resource]}. Upgrade your plan to add more. What it means
Plan-limit assertion that fires when creating a resource would meet or exceed the configured PLAN_LIMITS cap for the organization's plan (falls back to 'legacy' plan when none is set). It is a usage-quota FORBIDDEN error, not an auth problem.
Source
Thrown at apps/dokploy/server/api/utils/plan-limits.ts:69
const resourceLabels: Record<PlanLimitResource, string> = {
organization: "organizations",
member: "users",
environment: "environments per project",
volumeBackup: "volume backups per application",
databaseBackup: "backups per database",
scheduledJob: "scheduled jobs per service",
};
const assertLimitForPlan = (
plan: "hobby" | "startup" | "legacy" | null,
resource: PlanLimitResource,
currentCount: number,
) => {
const limit = PLAN_LIMITS[plan ?? "legacy"][resource];
if (currentCount >= limit) {
throw new TRPCError({
code: "FORBIDDEN",
message: `You've reached your plan's limit of ${limit} ${resourceLabels[resource]}. Upgrade your plan to add more.`,
});
}
};
export const assertOrganizationLimit = async (userId: string) => {
const plan = await getCurrentPlanForUser(userId);
const organizations = await db.query.organization.findMany({
where: eq(organization.ownerId, userId),
});
assertLimitForPlan(plan, "organization", organizations.length);
};
export const assertMemberLimit = async (organizationId: string) => {
const plan = await getCurrentPlan(organizationId);
const members = await db.query.member.findMany({
where: eq(member.organizationId, organizationId),View on GitHub (pinned to 546686ea35)
Solutions
- Upgrade the plan to raise the limit
- Delete unused resources of that type to get under the cap
- Verify currentCount (ensure stale/deleted records are purged or excluded) before upgrading
- Cache/assert limits client-side to warn before submission
Example fix
// before
await assertMemberLimit(org.plan, currentMembers);
// after
if (currentMembers >= PLAN_LIMITS[org.plan ?? 'legacy'].member) throw new Error('Limit reached — upgrade plan');
await assertMemberLimit(org.plan, currentMembers); Defensive patterns
Strategy: validation
Validate before calling
const limit = PLAN_LIMITS[plan ?? 'legacy'][resource];
if (currentCount >= limit) throw new Error(`Plan limit of ${limit} reached — upgrade or free up slots`); Type guard
const isUnderLimit = (plan: string | null, resource: PlanLimitResource, count: number): boolean => count < PLAN_LIMITS[plan ?? 'legacy'][resource];
Try / catch
catch (e) { if (e instanceof TRPCError && e.code === 'FORBIDDEN' && e.message.includes("plan's limit")) showUpgradeDialog(); throw e; } Prevention
- Show current usage vs limit in the UI before create actions
- Purge unused resources when near quota
- Assert limits client-side before submitting create forms
When it happens
Trigger: Calling assertOrganizationLimit / assertMemberLimit / assertEnvironmentLimit / assertVolumeBackupLimit / assertDatabaseBackupLimit / assertScheduledJobLimit when currentCount >= limit for the plan (e.g. adding another member on a plan capped at N members).
Common situations: Free/starter plan quotas hit; legacy self-hosted installs mapped to legacy limits; counts include soft-deleted resources inflating the number; users unaware a resource counts against the plan.
Related errors
- NOT_FOUND
- INNGEST_BASE_URL is required to list deployment jobs
- UNAUTHORIZED
- Failed to fetch models: ${errorText}
- BAD_REQUEST
AI-assisted analysis of Dokploy/dokploy@546686ea35 (2026-08-27).
Data as JSON: /api/errors/2969c9ba64173fed.
Report an issue: GitHub.