Dokploy/dokploy · error · TRPCError

UNAUTHORIZED

UNAUTHORIZED

Error message

You don't have access to this project

What it means

Role check in tag.assignToProject: non-owner/non-admin members may only assign tags to projects listed in their memberRecord.accessedProjects array. If the project is not in that array, UNAUTHORIZED is thrown even though the project and tag exist in the org.

Source

Thrown at apps/dokploy/server/api/routers/tag.ts:217

						eq(projects.organizationId, ctx.session.activeOrganizationId),
					),
				});

				if (!project) {
					throw new TRPCError({
						code: "NOT_FOUND",
						message:
							"Project not found or you don't have permission to modify it",
					});
				}

				// Verify the member has access to the project
				if (
					memberRecord.role !== "owner" &&
					memberRecord.role !== "admin" &&
					!memberRecord.accessedProjects.includes(input.projectId)
				) {
					throw new TRPCError({
						code: "UNAUTHORIZED",
						message: "You don't have access to this project",
					});
				}

				// Verify the tag belongs to the user's organization
				const tag = await db.query.tags.findFirst({
					where: and(
						eq(tags.tagId, input.tagId),
						eq(tags.organizationId, ctx.session.activeOrganizationId),
					),
				});

				if (!tag) {
					throw new TRPCError({
						code: "NOT_FOUND",
						message: "Tag not found or you don't have permission to use it",
					});

View on GitHub (pinned to 546686ea35)

Solutions

  1. Ask an owner/admin to grant the member access to the project (add it to accessedProjects)
  2. Perform the tag assignment as an owner/admin
  3. If access was recently granted, refresh the member record/session and retry

Example fix

// before
await api.tag.assignToProject({ projectId, tagId }); // as member
// after
// member checks their accessible projects first:
const me = await api.org.getMember(); // includes accessedProjects
if (me.accessedProjects.includes(projectId) || ['owner','admin'].includes(me.role)) {
  await api.tag.assignToProject({ projectId, tagId });
}
Defensive patterns

Strategy: type-guard

Validate before calling

const me = await api.org.getMember(); // role + accessedProjects
const canAssign = me.role === 'owner' || me.role === 'admin'
  || me.accessedProjects.includes(projectId);
if (canAssign) await api.tag.assignToProject({ projectId, tagId });

Type guard

const canAssignTagsToProject = (m: { role: string; accessedProjects: string[] }, projectId: string) =>
  m.role === 'owner' || m.role === 'admin' || m.accessedProjects.includes(projectId);

Try / catch

try { await assignToProject({ projectId, tagId }); }
catch (e) { if (e.shape?.data?.code === 'UNAUTHORIZED') requestProjectAccess(projectId); else throw e; }

Prevention

When it happens

Trigger: A member with role 'user' (or similar) calling tag.assignToProject for a project that was never granted to them via accessedProjects; owners/admins bypass this check.

Common situations: New team member tagging a project the org owner hasn't shared with them; project access revoked but the UI still shows tag controls; per-project permission model misunderstood as org-wide.

Related errors


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