fullstackhero/dotnet-starter-kit · error · ForbiddenException

System groups cannot be deleted.

Error message

System groups cannot be deleted.

What it means

DeleteGroup refuses to delete groups flagged IsSystemGroup, throwing ForbiddenException (HTTP 403). System groups are infrastructure-owned (seeded by migrations/platform code) and must not be removed by API callers.

Solutions

  1. Filter out IsSystemGroup groups before issuing deletes
  2. Hide system groups from destructive UI actions
  3. Catch 403 and skip the group in bulk operations

Example fix

// before
foreach (var g in allGroups) await api.DeleteGroup(g.Id);
// after
foreach (var g in allGroups.Where(g => !g.IsSystemGroup)) await api.DeleteGroup(g.Id);
Defensive patterns

Strategy: validation

Validate before calling

var group = await api.GetGroupByIdSafe(id);
if (group?.IsSystemGroup == true) skip delete;

Type guard

bool Deletable(GroupDto g) => !g.IsSystemGroup;

Try / catch

try { await api.DeleteGroup(id); }
catch (ApiException e) when (e.Status == 403) { logger.LogInformation("Group {Id} is a system group; skipped", id); }

Prevention

When it happens

Trigger: DELETE /groups/{id} where the target group has IsSystemGroup = true (e.g. built-in seeded groups).

Common situations: Bulk cleanup scripts iterating all groups including system ones; admin UI not hiding system groups; trying to 'reset' a tenant by deleting its built-in groups.

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/4612ded8414c667e. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Groups/DeleteGroup/DeleteGroupCommandHandler.cs:34

    public DeleteGroupCommandHandler(IdentityDbContext dbContext, ICurrentUser currentUser, IUserPermissionService userPermissionService)
    {
        _dbContext = dbContext;
        _currentUser = currentUser;
        _userPermissionService = userPermissionService;
    }

    public async ValueTask<Unit> Handle(DeleteGroupCommand command, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(command);

        var group = await _dbContext.Groups
            .FirstOrDefaultAsync(g => g.Id == command.Id, cancellationToken)
            ?? throw new NotFoundException($"Group with ID '{command.Id}' not found.");

        if (group.IsSystemGroup)
        {
            throw new ForbiddenException("System groups cannot be deleted.");
        }

        // Snapshot members before delete; soft-delete flips IsDeleted but membership rows
        // persist, so capture first for clarity.
        var memberIds = await _dbContext.UserGroups
            .Where(ug => ug.GroupId == command.Id)
            .Select(ug => ug.UserId)
            .ToListAsync(cancellationToken);

        // Soft delete via domain method
        group.Delete(_currentUser.GetUserId().ToString());

        await _dbContext.SaveChangesAsync(cancellationToken);

        // A deleted group can no longer contribute its roles to members' effective
        // permission sets — flush each member's cached entry.
        foreach (var userId in memberIds)
        {

View on GitHub (pinned to 3f2959e683)