{"record":{"id":"4612ded8414c667e","repo":"fullstackhero/dotnet-starter-kit","slug":"system-groups-cannot-be-deleted","errorCode":null,"errorMessage":"System groups cannot be deleted.","messagePattern":"System groups cannot be deleted\\.","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Features/v1/Groups/DeleteGroup/DeleteGroupCommandHandler.cs","lineNumber":34,"sourceCode":"\n    public DeleteGroupCommandHandler(IdentityDbContext dbContext, ICurrentUser currentUser, IUserPermissionService userPermissionService)\n    {\n        _dbContext = dbContext;\n        _currentUser = currentUser;\n        _userPermissionService = userPermissionService;\n    }\n\n    public async ValueTask<Unit> Handle(DeleteGroupCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        var group = await _dbContext.Groups\n            .FirstOrDefaultAsync(g => g.Id == command.Id, cancellationToken)\n            ?? throw new NotFoundException($\"Group with ID '{command.Id}' not found.\");\n\n        if (group.IsSystemGroup)\n        {\n            throw new ForbiddenException(\"System groups cannot be deleted.\");\n        }\n\n        // Snapshot members before delete; soft-delete flips IsDeleted but membership rows\n        // persist, so capture first for clarity.\n        var memberIds = await _dbContext.UserGroups\n            .Where(ug => ug.GroupId == command.Id)\n            .Select(ug => ug.UserId)\n            .ToListAsync(cancellationToken);\n\n        // Soft delete via domain method\n        group.Delete(_currentUser.GetUserId().ToString());\n\n        await _dbContext.SaveChangesAsync(cancellationToken);\n\n        // A deleted group can no longer contribute its roles to members' effective\n        // permission sets — flush each member's cached entry.\n        foreach (var userId in memberIds)\n        {","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/Groups/DeleteGroup/DeleteGroupCommandHandler.cs#L16-L52","documentation":"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.","triggerScenarios":"DELETE /groups/{id} where the target group has IsSystemGroup = true (e.g. built-in seeded groups).","commonSituations":"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.","solutions":["Filter out IsSystemGroup groups before issuing deletes","Hide system groups from destructive UI actions","Catch 403 and skip the group in bulk operations"],"exampleFix":"// before\nforeach (var g in allGroups) await api.DeleteGroup(g.Id);\n// after\nforeach (var g in allGroups.Where(g => !g.IsSystemGroup)) await api.DeleteGroup(g.Id);","handlingStrategy":"validation","validationCode":"var group = await api.GetGroupByIdSafe(id);\nif (group?.IsSystemGroup == true) skip delete;","typeGuard":"bool Deletable(GroupDto g) => !g.IsSystemGroup;","tryCatchPattern":"try { await api.DeleteGroup(id); }\ncatch (ApiException e) when (e.Status == 403) { logger.LogInformation(\"Group {Id} is a system group; skipped\", id); }","preventionTips":["Filter IsSystemGroup out of bulk delete targets","Hide delete actions for system groups in UI","Never attempt to 'reset' built-in groups via delete"],"tags":["http-403","forbidden","identity","system-groups"],"backgroundTag":"permission-denied","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}