langflow-ai/langflow · error · HTTPException
System roles cannot be deleted
Error message
System roles cannot be deleted
What it means
Raised by DELETE /api/v1/authz/roles/{role_id} when the target role has is_system=True. The three built-in roles (viewer / developer / admin) are seeded with is_system=True and are protected from deletion so the default role catalog stays stable for authorization plugins. Returns 400, not 404.
Source
Thrown at src/backend/base/langflow/api/v1/authz_roles.py:272
@router.delete("/{role_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_role(
role_id: UUID,
current_user: CurrentActiveUser,
session: DbSession,
) -> None:
"""Delete a custom role.
System roles cannot be deleted; roles with active assignments return 409
(delete the assignments first).
"""
_require_superuser(current_user)
role = await session.get(AuthzRole, role_id)
if role is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Role not found")
if role.is_system:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="System roles cannot be deleted",
)
assigned = (
await session.exec(select(AuthzRoleAssignment).where(AuthzRoleAssignment.role_id == role_id).limit(1))
).first()
if assigned is not None:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Role still has active assignments — revoke them before deleting",
)
role_name = role.name
await session.delete(role)
await session.commit()
await safe_invalidate_role(get_authorization_service(), role_id, op="role:delete")
await audit_decision(View on GitHub (pinned to 976ec789d2)
Solutions
- Skip roles with is_system=true when bulk-deleting (filter them out of your iteration)
- If you want a role with different semantics, create a new custom role instead of deleting a system one
- Check the role's is_system flag via GET /authz/roles before offering a delete button in the UI
Example fix
// before
for (const role of await listRoles()) {
await deleteRole(role.id); // 400 on system roles
}
// after
for (const role of await listRoles()) {
if (!role.is_system) await deleteRole(role.id);
} Defensive patterns
Strategy: type-guard
Validate before calling
const roles = await listRoles(); const deletable = roles.filter(r => !r.is_system);
Type guard
interface Role { id: string; name: string; is_system: boolean }
const isDeletableRole = (r: Role): boolean => !r.is_system; Prevention
- Filter is_system roles out of any bulk delete iteration
- Disable the delete affordance in UI when role.is_system is true
When it happens
Trigger: DELETE /authz/roles/{id} where the id belongs to a seeded system role (viewer, developer, or admin); scripts that iterate all roles and delete each one.
Common situations: Cleanup scripts that assume every role is deletable; attempting to remove the default roles before registering an authorization plugin that still references them; confusing the system 'admin' role with a custom role coincidentally named 'admin'.
Related errors
- Role still has active assignments — revoke them before delet
- System roles cannot be modified
- name cannot be null
- permissions cannot be null; pass an empty list to clear
- Name conflict — another role already uses this name
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/50990ff7249062b0.
Report an issue: GitHub.