{"record":{"id":"5adb398bcb640d91","repo":"fullstackhero/dotnet-starter-kit","slug":"group-with-name-command-name-already-exists","errorCode":null,"errorMessage":"Group with name '{command.Name}' already exists.","messagePattern":"Group with name '(.+?)' already exists\\.","errorType":"exception","errorClass":"CustomException","httpStatus":409,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Features/v1/Groups/CreateGroup/CreateGroupCommandHandler.cs","lineNumber":33,"sourceCode":"    private readonly ICurrentUser _currentUser;\n\n    public CreateGroupCommandHandler(IdentityDbContext dbContext, ICurrentUser currentUser)\n    {\n        _dbContext = dbContext;\n        _currentUser = currentUser;\n    }\n\n    public async ValueTask<GroupDto> Handle(CreateGroupCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        // Validate name is unique within tenant\n        var nameExists = await _dbContext.Groups\n            .AnyAsync(g => g.Name == command.Name, cancellationToken);\n\n        if (nameExists)\n        {\n            throw new CustomException($\"Group with name '{command.Name}' already exists.\", (IEnumerable<string>?)null, System.Net.HttpStatusCode.Conflict);\n        }\n\n        // Validate role IDs exist — fetch Id+Name in a single query to avoid a second roundtrip later\n        List<(string Id, string Name)> resolvedRoles = [];\n        if (command.RoleIds is { Count: > 0 })\n        {\n            var rawRoles = await _dbContext.Roles\n                .Where(r => command.RoleIds.Contains(r.Id))\n                .Select(r => new { r.Id, r.Name })\n                .ToListAsync(cancellationToken);\n            resolvedRoles = rawRoles.Select(r => (r.Id, r.Name!)).ToList();\n\n            var invalidRoleIds = command.RoleIds.Except(resolvedRoles.Select(r => r.Id)).ToList();\n            if (invalidRoleIds.Count > 0)\n            {\n                throw new NotFoundException($\"Roles not found: {string.Join(\", \", invalidRoleIds)}\");\n            }\n        }","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/Groups/CreateGroup/CreateGroupCommandHandler.cs#L15-L51","documentation":"CreateGroup enforces group-name uniqueness within the tenant; when a Group with command.Name already exists it throws CustomException with HttpStatusCode.Conflict (HTTP 409). Unlike NotFound/Forbidden, this surfaces as a 409 with the message in the response body.","triggerScenarios":"POST /groups with a Name identical to an existing group in the same tenant (exact string match).","commonSituations":"Double-submitting a create form; seeding scripts run twice; re-running an import that creates default groups.","solutions":["Query existing groups first and reuse the group if the name matches (idempotent create)","Catch the 409 and surface 'name already in use' in the UI to let the user pick another name","Uniquify the name (e.g. suffix) in seeding/import scripts"],"exampleFix":"// before\nawait api.CreateGroup(new CreateGroupCommand { Name = \"Admins\" });\n// after\nvar existing = await api.SearchGroups(\"Admins\");\nvar group = existing.FirstOrDefault() ?? await api.CreateGroup(new CreateGroupCommand { Name = \"Admins\" });","handlingStrategy":"try-catch","validationCode":"var clash = (await api.SearchGroups(name)).Any(g => g.Name.Equals(name, StringComparison.Ordinal));\nif (clash) throw new InvalidOperationException(\"Group name already in use\");","typeGuard":null,"tryCatchPattern":"try { await api.CreateGroup(cmd); }\ncatch (ApiException e) when (e.Status == 409) { NotifyUser(\"A group with that name already exists.\"); }","preventionTips":["Make create-group idempotent: search first, create only if absent","Guard seeding scripts against double execution","Uniquify names in import jobs"],"tags":["http-409","conflict","identity","groups","duplicate"],"backgroundTag":"entity-already-exists","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"}