langflow-ai/langflow · warning · HTTPException
Setting this parent would create a role hierarchy cycle
Error message
Setting this parent would create a role hierarchy cycle
What it means
Raised by PATCH /api/v1/authz/roles/{role_id} after _detect_parent_cycle walks the parent chain from the proposed parent and finds the role being edited already in it. Setting that parent would create a cycle in the role hierarchy, so the request is rejected with HTTP 400 before assignment.
Source
Thrown at src/backend/base/langflow/api/v1/authz_roles.py:197
fields_set = payload.model_fields_set
if "parent_role_id" in fields_set:
if payload.parent_role_id is None:
role.parent_role_id = None
else:
if payload.parent_role_id == role.id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="A role cannot be its own parent",
)
parent = await session.get(AuthzRole, payload.parent_role_id)
if parent is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="parent_role_id does not reference an existing role",
)
if await _detect_parent_cycle(session, role_id=role.id, proposed_parent_id=payload.parent_role_id):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Setting this parent would create a role hierarchy cycle",
)
role.parent_role_id = payload.parent_role_id
if "description" in fields_set:
# description is nullable on the DB side — None is a legitimate clear.
role.description = payload.description
if "name" in fields_set:
# name is NOT NULL + unique on the DB side; reject an explicit null at
# the boundary so the caller gets a clear 400 instead of an opaque
# IntegrityError that the catch block below mislabels as "Name conflict".
if payload.name is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="name cannot be null",
)View on GitHub (pinned to 976ec789d2)
Solutions
- Restructure the change: first set the intermediate role's parent to null (PATCH parent_role_id: null) to break the chain, then apply the intended parentage in topological order
- Verify with GET /authz/roles and walk parent ids client-side before submitting the move
- Apply hierarchy edits one hop at a time so any cycle is easy to localize
Example fix
// before
// A is ancestor of B; trying to make A's parent = B
await api.patch(`/authz/roles/${A}`, { parent_role_id: B }); // 400 cycle
// after
await api.patch(`/authz/roles/${A}`, { parent_role_id: null }); // detach
await api.patch(`/authz/roles/${B}`, { parent_role_id: A }); // desired order Defensive patterns
Strategy: validation
Validate before calling
// walk parent chain client-side; roles fetched from GET /authz/roles
function createsCycle(roles: Role[], roleId: string, proposedParent: string): boolean {
const byId = new Map(roles.map(r => [r.id, r]));
let cur = byId.get(proposedParent);
while (cur) {
if (cur.id === roleId) return true;
cur = cur.parent_role_id ? byId.get(cur.parent_role_id) : undefined;
}
return false;
} Type guard
const isAcyclicMove = (roles: Role[], roleId: string, parentId: string): boolean => !createsCycle(roles, roleId, parentId);
Prevention
- Simulate the parent change on the client hierarchy before PATCHing
- Apply hierarchy edits in topological order, detaching first when reordering
- Change one parent link per request so cycles are easy to localize
When it happens
Trigger: PATCH where role A's parent is set to B while B (or any of B's ancestors) already has A as an ancestor — e.g. A→B exists and you PATCH B with parent_role_id=A. Deep chains trigger it transitively, not just two-node loops.
Common situations: Reorganizing role hierarchies where admins move a parent beneath one of its descendants, or bulk import scripts that reorder hierarchy without topological ordering.
Related errors
- A role cannot be its own parent
- parent_role_id does not reference an existing role
- System roles cannot be modified
- Superuser required to administer roles.
- Role not found
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/0fe7abefb06463e0.
Report an issue: GitHub.