Dokploy/dokploy · error · TRPCError

FORBIDDEN

FORBIDDEN

Error message

Only the organization owner can create an organization

What it means

Role guard on organization creation: on self-hosted instances only users with role 'owner' or 'admin' may create organizations. On cloud (IS_CLOUD) this branch is skipped in favor of license/plan checks handled right after. Hitting it means the authenticated user's role is below admin (e.g. 'member' or 'user').

Source

Thrown at apps/dokploy/server/api/routers/organization.ts:34

import {
	invitation,
	member,
	organization,
	organizationRole,
	user,
} from "@/server/db/schema";
import { createTRPCRouter, protectedProcedure, withPermission } from "../trpc";
export const organizationRouter = createTRPCRouter({
	create: protectedProcedure
		.input(
			z.object({
				name: z.string(),
				logo: z.string().optional(),
			}),
		)
		.mutation(async ({ ctx, input }) => {
			if (ctx.user.role !== "owner" && ctx.user.role !== "admin" && !IS_CLOUD) {
				throw new TRPCError({
					code: "FORBIDDEN",
					message: "Only the organization owner can create an organization",
				});
			}

			if (IS_CLOUD) {
				await assertOrganizationLimit(ctx.user.id);
			}

			const result = await db
				.insert(organization)
				.values({
					...input,
					slug: nanoid(),
					createdAt: new Date(),
					ownerId: ctx.user.id,
				})
				.returning()

View on GitHub (pinned to 546686ea35)

Solutions

  1. Have an owner/admin create the organization, or promote the user's role first
  2. Log out and back in if the user's role was recently elevated so the session reflects it
  3. Verify ctx.user.role in the session payload (JWT) matches what you expect
  4. If you are the owner and still blocked, check the users table role value

Example fix

// before: member user calls
await trpc.organization.create.mutate({ name: 'New Org' }); // FORBIDDEN

// after: as owner/admin, or elevate first
// UPDATE users SET role='admin' WHERE id='<user-id>';  -- then re-login
await trpc.organization.create.mutate({ name: 'New Org' });
Defensive patterns

Strategy: validation

Validate before calling

const me = await trpc.auth.me.query();
if (!['owner', 'admin'].includes(me.role)) throw new Error('Ask an admin to create the organization');

Type guard

const canCreateOrg = (u: { role: string }, isCloud: boolean) => isCloud || u.role === 'owner' || u.role === 'admin';

Try / catch

try { await create(input); } catch (e) { if (getTRPCCode(e) === 'FORBIDDEN') showUpsellOrAskAdmin(); else throw e; }

Prevention

When it happens

Trigger: On a self-hosted Dokploy, a non-admin/non-owner user calls the organization.create mutation. The check is `ctx.user.role !== 'owner' && ctx.user.role !== 'admin' && !IS_CLOUD`.

Common situations: A team member who was invited to the instance tries to create their own organization; scripts authenticated with a lower-privilege user's session; role was changed after login but stale session role is used.

Related errors


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