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

  1. Upgrade the plan to raise the limit
  2. Delete unused resources of that type to get under the cap
  3. Verify currentCount (ensure stale/deleted records are purged or excluded) before upgrading
  4. 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

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


AI-assisted analysis of Dokploy/dokploy@546686ea35 (2026-08-27). Data as JSON: /api/errors/2969c9ba64173fed. Report an issue: GitHub.