{"record":{"id":"3b790dd26b2873f4","repo":"fullstackhero/dotnet-starter-kit","slug":"group-dm-requires-at-least-3-distinct-members","errorCode":null,"errorMessage":"Group DM requires at least 3 distinct members.","messagePattern":"Group DM requires at least 3 distinct members\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Modules/Chat/Modules.Chat/Domain/ChatChannel.cs","lineNumber":121,"sourceCode":"            IsPrivate = true,\n            DirectKey = $\"{lo}:{hi}\",\n            CreatedByUserId = userAId,\n            CreatedAtUtc = DateTime.UtcNow,\n        };\n        c._members.Add(ChannelMember.Create(c.Id, userAId, ChannelMemberRole.Member));\n        c._members.Add(ChannelMember.Create(c.Id, userBId, ChannelMemberRole.Member));\n        c.AddDomainEvent(DomainEvent.Create((id, ts) =>\n            new ChannelCreatedDomainEvent(c.Id, c.Type, null, userAId, id, ts)));\n        return c;\n    }\n\n    public static ChatChannel CreateGroupDm(IReadOnlyList<string> userIds, string creatorUserId)\n    {\n        ArgumentNullException.ThrowIfNull(userIds);\n        ArgumentException.ThrowIfNullOrWhiteSpace(creatorUserId);\n        if (userIds.Count < 3)\n        {\n            throw new ArgumentException(\"Group DM requires at least 3 distinct members.\", nameof(userIds));\n        }\n        if (userIds.Any(string.IsNullOrWhiteSpace))\n        {\n            throw new ArgumentException(\"All user ids must be non-empty.\", nameof(userIds));\n        }\n\n        var c = new ChatChannel\n        {\n            Id = Guid.CreateVersion7(),\n            Type = ChannelType.GroupMessage,\n            IsPrivate = true,\n            CreatedByUserId = creatorUserId,\n            CreatedAtUtc = DateTime.UtcNow,\n        };\n        foreach (var uid in userIds.Distinct(StringComparer.Ordinal))\n        {\n            var role = string.Equals(uid, creatorUserId, StringComparison.Ordinal)\n                ? ChannelMemberRole.Admin","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Domain/ChatChannel.cs#L103-L139","documentation":"ChatChannel.CreateGroupDm requires the userIds list to contain at least 3 entries; a shorter list throws ArgumentException(nameof(userIds)). Group DMs are by definition 3+ distinct participants, distinct from 1:1 DMs and named Channels.","triggerScenarios":"CreateGroupDm(userIds, creatorUserId) called with userIds.Count < 3 — e.g. forwarding a 2-element selection to the group-DM factory, or an empty/defaulted list when the user skips member selection.","commonSituations":"Frontend lets users submit a 'group' with only 2 picks (should use CreateDirect instead); batch script building channels with placeholder member arrays; migration code mapping old 2-person rooms into GroupMessage type.","solutions":["Use ChatChannel.CreateDirect for exactly two participants; reserve CreateGroupDm for 3+.","Validate userIds.Count >= 3 in the endpoint/handler before calling the factory.","Enforce a minimum member count (3) in the group-creation UI form.","Check client code that may truncate the selection array before sending."],"exampleFix":"// before\nvar channel = ChatChannel.CreateGroupDm(selectedIds, currentUserId);\n// after\nif (selectedIds.Count == 2)\n    return ChatChannel.CreateDirect(selectedIds[0], selectedIds[1]);\nif (selectedIds.Count < 3)\n    throw new ValidationException(\"Group DM needs at least 3 members\");\nvar channel = ChatChannel.CreateGroupDm(selectedIds, currentUserId);","handlingStrategy":"validation","validationCode":"// before calling the factory\nvar ids = userIds?.Where(id => !string.IsNullOrWhiteSpace(id)).Distinct().ToList()\n          ?? throw new ValidationException(\"userIds required\");\nif (ids.Count < 3)\n    throw new ValidationException(\"Group DM requires at least 3 members\");","typeGuard":"bool IsValidGroupDm(IReadOnlyList<string>? ids) =>\n    ids is not null && ids.Count >= 3 && ids.All(id => !string.IsNullOrWhiteSpace(id));","tryCatchPattern":"try { var c = ChatChannel.CreateGroupDm(userIds, creatorId); }\ncatch (ArgumentException ex) when (ex.ParamName == nameof(userIds)) { return Results.BadRequest(ex.Message); }","preventionTips":["Route 2-member selections to CreateDirect instead of CreateGroupDm.","Enforce a minimum of 3 selected members in the creation UI.","Deduplicate and filter the id list before calling the factory.","Note the count check runs before the emptiness check — fix size first."],"tags":["chat","argument","group-dm","validation","domain"],"backgroundTag":"value-out-of-range","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"}