{"record":{"id":"35886a526072bd4f","repo":"fullstackhero/dotnet-starter-kit","slug":"system-groups-cannot-be-modified","errorCode":null,"errorMessage":"System groups cannot be modified.","messagePattern":"System groups cannot be modified\\.","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"warning","filePath":"src/Modules/Identity/Modules.Identity/Features/v1/Groups/UpdateGroup/UpdateGroupCommandHandler.cs","lineNumber":36,"sourceCode":"\n    public UpdateGroupCommandHandler(IdentityDbContext dbContext, ICurrentUser currentUser, IUserPermissionService userPermissionService)\n    {\n        _dbContext = dbContext;\n        _currentUser = currentUser;\n        _userPermissionService = userPermissionService;\n    }\n\n    public async ValueTask<GroupDto> Handle(UpdateGroupCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        var group = await GetGroupAsync(command.Id, cancellationToken);\n\n        // System groups are framework-managed — name, description, default flag, and role\n        // assignments are all part of the seed contract that the startup syncer relies on.\n        if (group.IsSystemGroup)\n        {\n            throw new ForbiddenException(\"System groups cannot be modified.\");\n        }\n\n        await ValidateUniqueNameAsync(command.Id, command.Name, cancellationToken);\n        await ValidateRoleIdsAsync(command.RoleIds, cancellationToken);\n\n        var userId = _currentUser.GetUserId().ToString();\n        group.Update(command.Name, command.Description, userId);\n        group.SetAsDefault(command.IsDefault, userId);\n\n        var currentRoleIdsBefore = group.GroupRoles.Select(gr => gr.RoleId).ToHashSet();\n        var newRoleIds = UpdateRoleAssignments(group, command.RoleIds);\n        await _dbContext.SaveChangesAsync(cancellationToken);\n\n        // If the set of group→role assignments actually changed, every member's\n        // effective permission set may have shifted — invalidate each.\n        if (!currentRoleIdsBefore.SetEquals(newRoleIds))\n        {\n            var memberIds = await _dbContext.UserGroups","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/Groups/UpdateGroup/UpdateGroupCommandHandler.cs#L18-L54","documentation":"UpdateGroupCommandHandler throws this ForbiddenException when the target group has IsSystemGroup set. System groups are framework-managed: their name, description, default flag and role assignments form the seed contract the startup syncer depends on, so any modification attempt via the UpdateGroup endpoint is rejected before any validation or persistence happens.","triggerScenarios":"Sending an UpdateGroup command whose Id resolves to a group where Group.IsSystemGroup == true, regardless of which fields (name, description, IsDefault, RoleIds) the command tries to change.","commonSituations":"An admin UI lists seeded/system groups alongside normal groups and the user clicks 'Edit' on one; an import/sync script blindly iterates over all group IDs including system ones; a rename of the default 'Administrators'-style group seeded at startup.","solutions":["Pick a different, non-system group to modify — fetch the group first and skip or disable editing when IsSystemGroup is true.","If the system group's role assignments genuinely must change, do it by changing the seed/syncer definition (code-level seed contract), not through the UpdateGroup API.","Create a new regular group with the desired configuration instead of mutating the system group."],"exampleFix":"// before\nawait mediator.Send(new UpdateGroupCommand { Id = systemGroupId, Name = \"NewName\" });\n\n// after\nvar group = await groupApi.GetByIdAsync(systemGroupId);\nif (group.IsSystemGroup)\n{\n    // UI: disable edit button / show read-only view\n    return;\n}\nawait mediator.Send(new UpdateGroupCommand { Id = systemGroupId, Name = \"NewName\" });","handlingStrategy":"validation","validationCode":"var group = await groupApi.GetByIdAsync(id);\nif (group is null) throw new InvalidOperationException(\"Group not found\");\nif (group.IsSystemGroup)\n    throw new InvalidOperationException(\"System groups cannot be modified via UpdateGroup\");","typeGuard":"bool isEditable(Group g) => g is { IsSystemGroup: false };","tryCatchPattern":"try\n{\n    await mediator.Send(new UpdateGroupCommand { Id = id, /* ... */ });\n}\ncatch (ForbiddenException ex)\n{\n    // surface: \"This system group is managed by the framework and cannot be edited\"\n    logger.LogWarning(ex, \"Attempt to modify system group {GroupId}\", id);\n}","preventionTips":["Disable Edit/Delete actions for IsSystemGroup rows in the admin UI.","Never iterate all group IDs in bulk sync/import scripts — filter out system groups first.","Change system-group role assignments through the seed definition in code, not the API."],"tags":["identity","groups","forbidden","system-group"],"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"}