fullstackhero/dotnet-starter-kit · error · ArgumentException
Group DM requires at least 3 distinct members.
Error message
Group DM requires at least 3 distinct members.
What it means
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.
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.
Example fix
// before
var channel = ChatChannel.CreateGroupDm(selectedIds, currentUserId);
// after
if (selectedIds.Count == 2)
return ChatChannel.CreateDirect(selectedIds[0], selectedIds[1]);
if (selectedIds.Count < 3)
throw new ValidationException("Group DM needs at least 3 members");
var channel = ChatChannel.CreateGroupDm(selectedIds, currentUserId); Defensive patterns
Strategy: validation
Validate before calling
// before calling the factory
var ids = userIds?.Where(id => !string.IsNullOrWhiteSpace(id)).Distinct().ToList()
?? throw new ValidationException("userIds required");
if (ids.Count < 3)
throw new ValidationException("Group DM requires at least 3 members"); Type guard
bool IsValidGroupDm(IReadOnlyList<string>? ids) =>
ids is not null && ids.Count >= 3 && ids.All(id => !string.IsNullOrWhiteSpace(id)); Try / catch
try { var c = ChatChannel.CreateGroupDm(userIds, creatorId); }
catch (ArgumentException ex) when (ex.ParamName == nameof(userIds)) { return Results.BadRequest(ex.Message); } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- All user ids must be non-empty.
- ChannelId is required.
- Cannot start a DM with yourself.
- Only named Channels can be renamed.
- Only named Channels can change privacy.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/3b790dd26b2873f4.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Chat/Modules.Chat/Domain/ChatChannel.cs:121
IsPrivate = true,
DirectKey = $"{lo}:{hi}",
CreatedByUserId = userAId,
CreatedAtUtc = DateTime.UtcNow,
};
c._members.Add(ChannelMember.Create(c.Id, userAId, ChannelMemberRole.Member));
c._members.Add(ChannelMember.Create(c.Id, userBId, ChannelMemberRole.Member));
c.AddDomainEvent(DomainEvent.Create((id, ts) =>
new ChannelCreatedDomainEvent(c.Id, c.Type, null, userAId, id, ts)));
return c;
}
public static ChatChannel CreateGroupDm(IReadOnlyList<string> userIds, string creatorUserId)
{
ArgumentNullException.ThrowIfNull(userIds);
ArgumentException.ThrowIfNullOrWhiteSpace(creatorUserId);
if (userIds.Count < 3)
{
throw new ArgumentException("Group DM requires at least 3 distinct members.", nameof(userIds));
}
if (userIds.Any(string.IsNullOrWhiteSpace))
{
throw new ArgumentException("All user ids must be non-empty.", nameof(userIds));
}
var c = new ChatChannel
{
Id = Guid.CreateVersion7(),
Type = ChannelType.GroupMessage,
IsPrivate = true,
CreatedByUserId = creatorUserId,
CreatedAtUtc = DateTime.UtcNow,
};
foreach (var uid in userIds.Distinct(StringComparer.Ordinal))
{
var role = string.Equals(uid, creatorUserId, StringComparison.Ordinal)
? ChannelMemberRole.AdminView on GitHub (pinned to 3f2959e683)