{"record":{"id":"d88274884a4a3673","repo":"Dokploy/dokploy","slug":"forbidden-d88274","errorCode":"FORBIDDEN","errorMessage":"Only the organization owner can create an organization","messagePattern":"Only the organization owner can create an organization","errorType":"exception","errorClass":"TRPCError","httpStatus":403,"severity":"error","filePath":"apps/dokploy/server/api/routers/organization.ts","lineNumber":34,"sourceCode":"import {\n\tinvitation,\n\tmember,\n\torganization,\n\torganizationRole,\n\tuser,\n} from \"@/server/db/schema\";\nimport { createTRPCRouter, protectedProcedure, withPermission } from \"../trpc\";\nexport const organizationRouter = createTRPCRouter({\n\tcreate: protectedProcedure\n\t\t.input(\n\t\t\tz.object({\n\t\t\t\tname: z.string(),\n\t\t\t\tlogo: z.string().optional(),\n\t\t\t}),\n\t\t)\n\t\t.mutation(async ({ ctx, input }) => {\n\t\t\tif (ctx.user.role !== \"owner\" && ctx.user.role !== \"admin\" && !IS_CLOUD) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: \"FORBIDDEN\",\n\t\t\t\t\tmessage: \"Only the organization owner can create an organization\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (IS_CLOUD) {\n\t\t\t\tawait assertOrganizationLimit(ctx.user.id);\n\t\t\t}\n\n\t\t\tconst result = await db\n\t\t\t\t.insert(organization)\n\t\t\t\t.values({\n\t\t\t\t\t...input,\n\t\t\t\t\tslug: nanoid(),\n\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\townerId: ctx.user.id,\n\t\t\t\t})\n\t\t\t\t.returning()","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/Dokploy/dokploy/blob/546686ea3587f12ec5652217dedd9f7960fb6d15/apps/dokploy/server/api/routers/organization.ts#L16-L52","documentation":"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').","triggerScenarios":"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`.","commonSituations":"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.","solutions":["Have an owner/admin create the organization, or promote the user's role first","Log out and back in if the user's role was recently elevated so the session reflects it","Verify ctx.user.role in the session payload (JWT) matches what you expect","If you are the owner and still blocked, check the users table role value"],"exampleFix":"// before: member user calls\nawait trpc.organization.create.mutate({ name: 'New Org' }); // FORBIDDEN\n\n// after: as owner/admin, or elevate first\n// UPDATE users SET role='admin' WHERE id='<user-id>';  -- then re-login\nawait trpc.organization.create.mutate({ name: 'New Org' });","handlingStrategy":"validation","validationCode":"const me = await trpc.auth.me.query();\nif (!['owner', 'admin'].includes(me.role)) throw new Error('Ask an admin to create the organization');","typeGuard":"const canCreateOrg = (u: { role: string }, isCloud: boolean) => isCloud || u.role === 'owner' || u.role === 'admin';","tryCatchPattern":"try { await create(input); } catch (e) { if (getTRPCCode(e) === 'FORBIDDEN') showUpsellOrAskAdmin(); else throw e; }","preventionTips":["Hide the 'New Organization' button for non-admin roles","Re-login after role elevation","Check session.role in devtools before debugging 403s"],"tags":["trpc","authorization","roles","rbac","self-hosted"],"backgroundTag":"insufficient-permissions","analyzedSha":"546686ea3587f12ec5652217dedd9f7960fb6d15","analyzedAt":"2026-08-27T05:18:58.095Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}