fullstackhero/dotnet-starter-kit · error · CustomException

Group with name ' ' already exists.

Error message

Group with name '{name}' already exists.

What it means

ValidateUniqueNameAsync checks whether another group (Id != excludeId) already has the requested Name and, if so, throws CustomException with HTTP 409 Conflict. Group names are unique within the store, so a rename to an existing name is rejected.

Solutions

  1. Choose a different, unique group name for the update.
  2. Add a client-side uniqueness check (list groups, compare names case-insensitively) before submitting the update.
  3. If the name should be free but isn't, find the group holding it and rename/delete that group first, or merge the two groups.

Example fix

// before
await mediator.Send(new UpdateGroupCommand { Id = id, Name = "Sales" }); // 409: 'Sales' exists

// after
var groups = await groupApi.ListAsync();
if (groups.Any(g => g.Name.Equals("Sales", StringComparison.OrdinalIgnoreCase) && g.Id != id))
{
    showValidationError("A group named 'Sales' already exists.");
    return;
}
await mediator.Send(new UpdateGroupCommand { Id = id, Name = "Sales" });
Defensive patterns

Strategy: validation

Validate before calling

var taken = (await groupApi.ListAsync())
    .Any(g => g.Name.Trim().Equals(newName.Trim(), StringComparison.OrdinalIgnoreCase) && g.Id != groupId);
if (taken) showValidationError("Group name already in use.");

Type guard

bool isUniqueName(IEnumerable<Group> groups, string name, Guid excludeId) =>
    !groups.Any(g => g.Id != excludeId && string.Equals(g.Name, name, StringComparison.OrdinalIgnoreCase));

Try / catch

try
{
    await mediator.Send(new UpdateGroupCommand { Id = id, Name = newName });
}
catch (CustomException ex) when (ex.StatusCode == HttpStatusCode.Conflict)
{
    showValidationError("A group with that name already exists. Choose another name.");
}

Prevention

When it happens

Trigger: Sending UpdateGroupCommand whose Name matches a different existing group in the same tenant — typically a rename colliding with an already-taken group name.

Common situations: Two admins concurrently rename groups to the same value; a UI that doesn't pre-check name availability; renaming a group back to a name it previously held before being renamed away while another group took it.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/6733f1c4d8760a29. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Groups/UpdateGroup/UpdateGroupCommandHandler.cs:82

        return await BuildResponseAsync(group, newRoleIds, cancellationToken);
    }

    private async Task<Group> GetGroupAsync(Guid id, CancellationToken cancellationToken)
    {
        return await _dbContext.Groups
            .Include(g => g.GroupRoles)
            .FirstOrDefaultAsync(g => g.Id == id, cancellationToken)
            ?? throw new NotFoundException($"Group with ID '{id}' not found.");
    }

    private async Task ValidateUniqueNameAsync(Guid excludeId, string name, CancellationToken cancellationToken)
    {
        var nameExists = await _dbContext.Groups
            .AnyAsync(g => g.Name == name && g.Id != excludeId, cancellationToken);

        if (nameExists)
        {
            throw new CustomException($"Group with name '{name}' already exists.", (IEnumerable<string>?)null, System.Net.HttpStatusCode.Conflict);
        }
    }

    private async Task ValidateRoleIdsAsync(IReadOnlyList<string>? roleIds, CancellationToken cancellationToken)
    {
        if (roleIds is not { Count: > 0 })
        {
            return;
        }

        var existingRoleIds = await _dbContext.Roles
            .Where(r => roleIds.Contains(r.Id))
            .Select(r => r.Id)
            .ToListAsync(cancellationToken);

        var invalidRoleIds = roleIds.Except(existingRoleIds).ToList();
        if (invalidRoleIds.Count > 0)
        {

View on GitHub (pinned to 3f2959e683)