{"record":{"id":"5638e4d5d0502b74","repo":"n8n-io/n8n","slug":"project-roles-are-managed-automatically-and-cannot","errorCode":null,"errorMessage":"Project roles are managed automatically and cannot be changed manually","messagePattern":"Project roles are managed automatically and cannot be changed manually","errorType":"exception","errorClass":"ForbiddenError","httpStatus":403,"severity":"error","filePath":"packages/cli/src/controllers/project.controller.ts","lineNumber":282,"sourceCode":"\t\t_res: Response,\n\t\t@Body payload: UpdateProjectDto,\n\t\t@Param('projectId') projectId: string,\n\t) {\n\t\tawait this.projectsService.updateProject(projectId, payload);\n\t\tthis.eventService.emit('team-project-updated', {\n\t\t\tuserId: req.user.id,\n\t\t\trole: req.user.role.slug,\n\t\t\tprojectId,\n\t\t\t...(payload.customTelemetryTags !== undefined\n\t\t\t\t? { otelProjectCustomTagsCount: payload.customTelemetryTags.length }\n\t\t\t\t: {}),\n\t\t});\n\t}\n\n\t/** Throws when project roles are provisioned automatically, so manual membership changes are disallowed. */\n\tprivate async assertProjectRolesNotManaged() {\n\t\tif (await this.provisioningService.isProjectRoleManaged()) {\n\t\t\tthrow new ForbiddenError(\n\t\t\t\t'Project roles are managed automatically and cannot be changed manually',\n\t\t\t);\n\t\t}\n\t}\n\n\t@Post('/:projectId/users')\n\t@ProjectScope('project:update')\n\tasync addProjectUsers(\n\t\treq: AuthenticatedRequest,\n\t\tres: Response,\n\t\t@Param('projectId') projectId: string,\n\t\t@Body payload: AddUsersToProjectDto,\n\t) {\n\t\tawait this.assertProjectRolesNotManaged();\n\t\ttry {\n\t\t\tconst { added, conflicts, project } =\n\t\t\t\tawait this.projectsService.addUsersWithConflictSemantics(projectId, payload.relations);\n","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/controllers/project.controller.ts#L264-L300","documentation":"When project roles are provisioned automatically by an external system (SSO/SCIM role mapping), n8n locks down all manual membership mutations. assertProjectRolesNotManaged() throws 403 ForbiddenError if provisioningService.isProjectRoleManaged() returns true.","triggerScenarios":"Any controller action guarded by assertProjectRolesNotManaged() — adding, changing, or deleting project users — invoked while provisioning reports that roles are externally managed.","commonSituations":"Enterprise SSO/SCIM with role mapping enabled; an admin attempts to add or reassign a project member through the UI or REST API while managed-roles mode is on.","solutions":["Manage project membership through the SSO/SCIM provider's role/group mappings instead of the API.","If manual control is genuinely required, disable managed-roles mode (and understand the implications) via the provisioning/SSO configuration."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// If a metadata endpoint exposes managed-roles mode, check it before attempting mutations.\nasync function isManaged(getProvisioningState) {\n  return Boolean((await getProvisioningState())?.projectRoleManaged);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.post(`/projects/${projectId}/users`, payload);\n} catch (e) {\n  if (e.status === 403 && /managed automatically/.test(e.message)) {\n    // instruct admin to use the SSO/SCIM provider for membership changes\n  } else { throw e; }\n}","preventionTips":["When SSO/SCIM role mapping is on, drive memberships from the IdP.","Surface managed-roles state in the UI to disable manual controls."],"tags":["rbac","sso","scim","provisioning","projects"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}