medusajs/medusa · error · Error

Cannot create role parent relationship: a role cannot be its

Error message

Cannot create role parent relationship: a role cannot be its own parent (role_id: ${role_id})

What it means

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.

Source

Thrown at packages/modules/rbac/src/services/rbac-module-service.ts:250

      for (const role of roles) {
        role.policies = policiesByRole.get(role.id) || []
      }
    }

    return [roles as unknown as RbacRoleDTO[], count]
  }

  @InjectManager()
  // @ts-expect-error
  async createRbacRoleParents(
    data: CreateRbacRoleParentDTO[],
    @MedusaContext() sharedContext: Context = {}
  ): Promise<RbacRoleParentDTO[]> {
    for (const parent of data) {
      const { role_id, parent_id } = parent

      if (role_id === parent_id) {
        throw new Error(
          `Cannot create role parent relationship: a role cannot be its own parent (role_id: ${role_id})`
        )
      }

      const wouldCreateCycle = await this.rbacRepository_.checkForCycle(
        role_id,
        parent_id,
        sharedContext
      )

      if (wouldCreateCycle) {
        throw new Error(
          `Cannot create role parent relationship: this would create a circular dependency (role_id: ${role_id}, parent_id: ${parent_id})`
        )
      }
    }

    return await super.createRbacRoleParents(data, sharedContext)

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Fix the caller to omit or choose a different parent_id when it equals role_id
  2. If a role should have no parent, delete the existing parent relation instead of creating a self-relation
  3. Add client-side validation to disable selecting the role itself in parent pickers

Example fix

// before
await rbacService.createRbacRoleParents([
  { role_id: roleId, parent_id: roleId },
])
// after
await rbacService.createRbacRoleParents([
  { role_id: roleId, parent_id: differentRoleId },
])
Defensive patterns

Strategy: validation

Validate before calling

function assertNotSelfParent(rels: { role_id: string; parent_id: string }[]) {
  const bad = rels.filter((r) => r.role_id === r.parent_id)
  if (bad.length) throw new Error(`Self-parent for roles: ${bad.map((b) => b.role_id).join(", ")}`)
}

Type guard

function isValidParentRelation(rel: { role_id: string; parent_id: string }): boolean {
  return rel.role_id !== rel.parent_id
}

Try / catch

try {
  await rbacService.createRbacRoleParents(payload)
} catch (e) {
  if (/cannot be its own parent/.test(e.message)) {
    payload = payload.filter((r) => r.role_id !== r.parent_id)
  } else throw e
}

Prevention

When it happens

Trigger: 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' }.

Common situations: 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.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/f395a70e0690d8f8. Report an issue: GitHub.