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

  1. Skip roles with is_system=true when bulk-deleting (filter them out of your iteration)
  2. If you want a role with different semantics, create a new custom role instead of deleting a system one
  3. 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

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


AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14). Data as JSON: /api/errors/50990ff7249062b0. Report an issue: GitHub.