{"record":{"id":"f395a70e0690d8f8","repo":"medusajs/medusa","slug":"cannot-create-role-parent-relationship-a-role-can","errorCode":null,"errorMessage":"Cannot create role parent relationship: a role cannot be its own parent (role_id: ${role_id})","messagePattern":"Cannot create role parent relationship: a role cannot be its own parent \\(role_id: (.+?)\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/modules/rbac/src/services/rbac-module-service.ts","lineNumber":250,"sourceCode":"      for (const role of roles) {\n        role.policies = policiesByRole.get(role.id) || []\n      }\n    }\n\n    return [roles as unknown as RbacRoleDTO[], count]\n  }\n\n  @InjectManager()\n  // @ts-expect-error\n  async createRbacRoleParents(\n    data: CreateRbacRoleParentDTO[],\n    @MedusaContext() sharedContext: Context = {}\n  ): Promise<RbacRoleParentDTO[]> {\n    for (const parent of data) {\n      const { role_id, parent_id } = parent\n\n      if (role_id === parent_id) {\n        throw new Error(\n          `Cannot create role parent relationship: a role cannot be its own parent (role_id: ${role_id})`\n        )\n      }\n\n      const wouldCreateCycle = await this.rbacRepository_.checkForCycle(\n        role_id,\n        parent_id,\n        sharedContext\n      )\n\n      if (wouldCreateCycle) {\n        throw new Error(\n          `Cannot create role parent relationship: this would create a circular dependency (role_id: ${role_id}, parent_id: ${parent_id})`\n        )\n      }\n    }\n\n    return await super.createRbacRoleParents(data, sharedContext)","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/modules/rbac/src/services/rbac-module-service.ts#L232-L268","documentation":"Thrown by the RBAC module when creating role-parent relationships if a role is specified as its own parent (role_id === parent_id). This guard runs before persistence in createRbacRoleParents, preventing self-referential hierarchy rows that would break permission inheritance traversal.","triggerScenarios":"Calling createRbacRoleParents (directly or via workflows like setRoleParentStep / createRbacRoleParents workflow) with a payload where role_id equals parent_id, e.g. { role_id: 'role_1', parent_id: 'role_1' }.","commonSituations":"UI bugs that preselect the current role as its own parent, scripts copying role hierarchies that accidentally include the root role as its own parent, or defaulting parent_id to role_id when no parent is chosen.","solutions":["Fix the caller to omit or choose a different parent_id when it equals role_id","If a role should have no parent, delete the existing parent relation instead of creating a self-relation","Add client-side validation to disable selecting the role itself in parent pickers"],"exampleFix":"// before\nawait rbacService.createRbacRoleParents([\n  { role_id: roleId, parent_id: roleId },\n])\n// after\nawait rbacService.createRbacRoleParents([\n  { role_id: roleId, parent_id: differentRoleId },\n])","handlingStrategy":"validation","validationCode":"function assertNotSelfParent(rels: { role_id: string; parent_id: string }[]) {\n  const bad = rels.filter((r) => r.role_id === r.parent_id)\n  if (bad.length) throw new Error(`Self-parent for roles: ${bad.map((b) => b.role_id).join(\", \")}`)\n}","typeGuard":"function isValidParentRelation(rel: { role_id: string; parent_id: string }): boolean {\n  return rel.role_id !== rel.parent_id\n}","tryCatchPattern":"try {\n  await rbacService.createRbacRoleParents(payload)\n} catch (e) {\n  if (/cannot be its own parent/.test(e.message)) {\n    payload = payload.filter((r) => r.role_id !== r.parent_id)\n  } else throw e\n}","preventionTips":["Filter self-relations out of bulk payloads before submit","Disable the current role in parent-selection UIs","Validate in a workflow step before the service call"],"tags":["rbac","roles","hierarchy","validation"],"backgroundTag":"self-referencing-relation","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}