{"record":{"id":"b4f65e0039078068","repo":"medusajs/medusa","slug":"cannot-update-role-parent-relationship-this-would","errorCode":null,"errorMessage":"Cannot update role parent relationship: this would create a circular dependency (role_id: ${role_id}, parent_id: ${parent_id})","messagePattern":"Cannot update role parent relationship: this would create a circular dependency \\(role_id: (.+?), parent_id: (.+?)\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/modules/rbac/src/services/rbac-module-service.ts","lineNumber":294,"sourceCode":"  ): Promise<RbacRoleParentDTO[]> {\n    for (const parent of data) {\n      const { role_id, parent_id } = parent\n\n      if (parent_id) {\n        if (role_id === parent_id) {\n          throw new Error(\n            `Cannot update 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 update role parent relationship: this would create a circular dependency (role_id: ${role_id}, parent_id: ${parent_id})`\n          )\n        }\n      }\n    }\n\n    return await super.updateRbacRoleParents(data, sharedContext)\n  }\n}\n","sourceCodeStart":276,"sourceCodeEnd":304,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/modules/rbac/src/services/rbac-module-service.ts#L276-L304","documentation":"Thrown by the RBAC module when an update to a role-parent relationship would create a circular dependency in the hierarchy. checkForCycle is evaluated with the proposed role_id/parent_id before the update is persisted.","triggerScenarios":"Calling updateRbacRoleParents where the new parent_id is a descendant of role_id — e.g. after A->B exists, updating B's relation to point at A.","commonSituations":"Re-parenting operations that move a subtree under one of its own descendants, hierarchy imports run as updates, or races where two concurrent re-parents each pass the check individually.","solutions":["Choose a new parent outside the current role's descendant chain","If restructuring a whole subtree, re-point children first (bottom-up) so no intermediate state is cyclic","Verify the intended tree shape in a dry-run/topological pass before applying updates"],"exampleFix":"// before\n// existing: A parent of B\nawait rbacService.updateRbacRoleParents([\n  { id: bRelationId, role_id: b, parent_id: a }, // cycle\n])\n// after\nawait rbacService.updateRbacRoleParents([\n  { id: bRelationId, role_id: b, parent_id: rootRole },\n])","handlingStrategy":"validation","validationCode":"async function assertNoUpdateCycle(rbac: any, u: { role_id: string; parent_id: string }) {\n  const seen = new Set<string>()\n  let current = u.parent_id\n  while (current && !seen.has(current)) {\n    if (current === u.role_id) throw new Error('Update would create a cycle')\n    seen.add(current)\n    const parents = await rbac.listRbacRoleParents({ role_id: current })\n    current = parents[0]?.parent_id\n  }\n}","typeGuard":"async function isNonCyclicUpdate(rbac: any, u: { role_id: string; parent_id: string }): Promise<boolean> {\n  const seen = new Set<string>()\n  let cur = u.parent_id\n  while (cur && !seen.has(cur)) {\n    if (cur === u.role_id) return false\n    seen.add(cur)\n    cur = (await rbac.listRbacRoleParents({ role_id: cur }))[0]?.parent_id\n  }\n  return true\n}","tryCatchPattern":"try {\n  await rbacService.updateRbacRoleParents(updates)\n} catch (e) {\n  if (/circular dependency/.test(e.message)) {\n    // alert: re-parenting attempted under own subtree; pick another parent\n  } else throw e\n}","preventionTips":["Restructure subtrees bottom-up so intermediate states stay acyclic","Preview re-parent operations against the current tree before applying","Lock hierarchy edits during bulk reorganizations"],"tags":["rbac","roles","cycle-detection","update"],"backgroundTag":"circular-dependency-detected","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}