fullstackhero/dotnet-starter-kit · error · CustomException
System role permissions are managed by the framework and…
Error message
System role permissions are managed by the framework and cannot be modified.
What it means
EnsureNotSystemRole throws CustomException with HTTP 400 when the target role's name matches RoleConstants.IsDefault — i.e. a built-in framework role (like Basic/Admin) whose definition is managed by the framework.
Solutions
- Create a custom role with the desired name/permissions instead of editing the default one
- Remove default roles from editable lists in the UI (mark as system)
- If truly needed, change RoleConstants/seed data — not via the API
Example fix
// before await roleService.UpdatePermissionsAsync(basicRoleId, limitedPerms, ct); // after var customRoleId = await roleService.CreateOrUpdateRoleAsync(null, "Limited", "Custom limited role", ct); await roleService.UpdatePermissionsAsync(customRoleId, limitedPerms, ct);
Defensive patterns
Strategy: validation
Validate before calling
var role = await roleService.GetRoleAsync(roleId, ct);
if (role is not null && RoleConstants.IsDefault(role.Name))
throw new InvalidOperationException($"{role.Name} is a system role and cannot be modified"); Type guard
public static bool IsSystemRole(RoleDto? r) => r is not null && RoleConstants.IsDefault(r.Name);
Try / catch
try { await roleService.UpdatePermissionsAsync(roleId, perms, ct); }
catch (CustomException ex) when (ex.Message.Contains("System role")) { return Results.BadRequest(ex.Message); } Prevention
- Maintain a client-side allowlist of editable roles
- Hide delete/rename actions for roles flagged as system
- Never re-purpose default roles for custom permission sets
When it happens
Trigger: CreateOrUpdateRoleAsync renaming a default role, DeleteRoleAsync deleting one, or UpdatePermissionsAsync mutating the permissions of a default role.
Common situations: Trying to tune down the built-in Admin role's permissions; seeding code that modifies stock roles; renaming 'Basic' for branding.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Roles not found
- Roles not found
- Role store not configured. Ensure .AddRoles
- role not found
- operation failed
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/f4dfef99a3fce395.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Roles/RoleService.cs:188
EnsureNotSystemRole(role.Name, "System role permissions are managed by the framework and cannot be modified.");
FilterRootPermissions(permissions);
var currentClaims = await roleManager.GetClaimsAsync(role);
await RemoveRevokedPermissionsAsync(role, currentClaims, permissions, cancellationToken);
await AddNewPermissionsAsync(role, currentClaims, permissions, cancellationToken);
// Permissions on the role just changed — every user reachable through this
// role (directly or via group membership) now has a stale cache entry.
await InvalidateAffectedUsersAsync(roleId, cancellationToken).ConfigureAwait(false);
return "permissions updated";
}
private static void EnsureNotSystemRole(string? roleName, string message)
{
if (!string.IsNullOrEmpty(roleName) && RoleConstants.IsDefault(roleName))
{
throw new CustomException(message, Array.Empty<string>(), HttpStatusCode.BadRequest);
}
}
private void FilterRootPermissions(List<string> permissions)
{
if (multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id == MultitenancyConstants.Root.Id)
{
// The root operator may manage root-only permissions.
return;
}
// Strip every permission flagged IsRoot in the registry. (A prior prefix check on "Permissions.Root."
// was a no-op — no root perm uses that prefix — letting a tenant admin grant themselves root perms.)
var rootOnly = PermissionConstants.Root.Select(p => p.Name).ToHashSet(StringComparer.Ordinal);
permissions.RemoveAll(rootOnly.Contains);
}
private async Task RemoveRevokedPermissionsAsync(FshRole role, IList<System.Security.Claims.Claim> currentClaims, List<string> permissions, CancellationToken cancellationToken = default)View on GitHub (pinned to 3f2959e683)