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

  1. Create a custom role with the desired name/permissions instead of editing the default one
  2. Remove default roles from editable lists in the UI (mark as system)
  3. 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

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


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)