medusajs/medusa · error · Error
Cannot update role parent relationship: a role cannot be its
Error message
Cannot update role parent relationship: a role cannot be its own parent (role_id: ${role_id}) What it means
Thrown by the RBAC module when updating an existing role-parent relationship if the update sets parent_id equal to role_id, making the role its own parent. It fires inside updateRbacRoleParents before any write, only when parent_id is provided in the update payload.
Source
Thrown at packages/modules/rbac/src/services/rbac-module-service.ts:282
)
}
}
return await super.createRbacRoleParents(data, sharedContext)
}
@InjectManager()
// @ts-expect-error
async updateRbacRoleParents(
data: UpdateRbacRoleParentDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<RbacRoleParentDTO[]> {
for (const parent of data) {
const { role_id, parent_id } = parent
if (parent_id) {
if (role_id === parent_id) {
throw new Error(
`Cannot update 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 update role parent relationship: this would create a circular dependency (role_id: ${role_id}, parent_id: ${parent_id})`
)
}
}
}
View on GitHub (pinned to 5e06e544a2)
Solutions
- Send parent_id of a different role, or omit parent_id entirely if it should not change
- To remove a parent, use the delete relation API rather than setting parent_id = role_id
- Guard the edit form against submitting the current role as its own parent
Example fix
// before
await rbacService.updateRbacRoleParents([
{ id: relId, role_id: roleId, parent_id: roleId },
])
// after
await rbacService.updateRbacRoleParents([
{ id: relId, role_id: roleId, parent_id: otherRoleId },
]) Defensive patterns
Strategy: validation
Validate before calling
function assertUpdateNotSelfParent(updates: { role_id?: string; parent_id?: string }[]) {
for (const u of updates) {
if (u.parent_id && u.role_id && u.role_id === u.parent_id) {
throw new Error(`Update sets role ${u.role_id} as its own parent`)
}
}
} Type guard
function isSafeParentUpdate(u: { role_id?: string; parent_id?: string }): boolean {
return !u.parent_id || !u.role_id || u.role_id !== u.parent_id
} Try / catch
try {
await rbacService.updateRbacRoleParents(updates)
} catch (e) {
if (/cannot be its own parent/.test(e.message)) {
updates = updates.filter(isSafeParentUpdate)
await rbacService.updateRbacRoleParents(updates)
} else throw e
} Prevention
- Omit parent_id from PATCH payloads when unchanged
- Map empty parent selection to a delete-relation call, not parent_id=role_id
When it happens
Trigger: Calling updateRbacRoleParents with { id, role_id: X, parent_id: X } — i.e. an update payload whose new parent equals the role being updated.
Common situations: Edit forms that submit the role itself as parent when the parent field is left blank but defaulted, or PATCH payloads echoing back role_id into parent_id.
Related errors
- Cannot create role parent relationship: a role cannot be its
- Cannot update role parent relationship: this would create a
- Role with id: ${req.params.id} not found
- Cannot create role parent relationship: this would create a
- --paths must be a directory - ${additionalPath}
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/c7d685bbbb524f26.
Report an issue: GitHub.