fullstackhero/dotnet-starter-kit · error · ForbiddenException

Users cannot be removed from a default group.

Error message

Users cannot be removed from a default group.

What it means

RemoveUserFromGroup blocks removal from groups flagged IsDefault (e.g. the seeded "All Users" group) by throwing ForbiddenException (HTTP 403). Default groups guarantee every tenant user is a member; removing members would break that invariant for later registrants.

Solutions

  1. Skip IsDefault groups in bulk membership-removal logic
  2. For offboarding, deactivate/delete the user account instead of removing them from the default group
  3. Catch 403 and continue in bulk operations, logging the skipped pair

Example fix

// before
foreach (var g in user.Groups) await api.RemoveUserFromGroup(g.Id, userId);
// after
foreach (var g in user.Groups.Where(g => !g.IsDefault)) await api.RemoveUserFromGroup(g.Id, userId);
Defensive patterns

Strategy: validation

Validate before calling

var targets = user.Groups.Where(g => !g.IsDefault);
foreach (var g in targets) await api.RemoveUserFromGroup(g.Id, userId);

Type guard

bool Removable(GroupDto g) => !g.IsDefault;

Try / catch

try { await api.RemoveUserFromGroup(groupId, userId); }
catch (ApiException e) when (e.Status == 403) { logger.LogInformation("Skipped default group {GroupId}", groupId); }

Prevention

When it happens

Trigger: DELETE /groups/{groupId}/users/{userId} where membership.Group.IsDefault is true — attempting to kick a user out of the tenant's default group.

Common situations: Bulk cleanup scripts iterating every group's members; offboarding flows that try to strip all group memberships; UI not marking default groups as read-only.

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

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Groups/RemoveUserFromGroup/RemoveUserFromGroupCommandHandler.cs:38

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

        var membership = await _dbContext.UserGroups
            .Include(ug => ug.Group)
            .FirstOrDefaultAsync(ug => ug.GroupId == command.GroupId && ug.UserId == command.UserId, cancellationToken);

        if (membership is null)
        {
            throw new NotFoundException($"User '{command.UserId}' is not a member of group '{command.GroupId}'.");
        }

        // Default groups (e.g. seeded "All Users") require every tenant user to be a member, so
        // removing one breaks that invariant and leaves later registrants in a half-populated group.
        if (membership.Group is not null && membership.Group.IsDefault)
        {
            throw new ForbiddenException("Users cannot be removed from a default group.");
        }

        _dbContext.UserGroups.Remove(membership);
        await _dbContext.SaveChangesAsync(cancellationToken);

        // Leaving a group may revoke roles the user only held through this group —
        // invalidate so the cached permission set is rebuilt on next request.
        await _userPermissionService.InvalidatePermissionCacheAsync(command.UserId, cancellationToken).ConfigureAwait(false);

        return Unit.Value;
    }
}

View on GitHub (pinned to 3f2959e683)