fullstackhero/dotnet-starter-kit · warning · CustomException
Cannot DM yourself.
Error message
Cannot DM yourself.
What it means
FindOrCreateDmCommandHandler rejects a DM creation request whose participant list includes the calling user themself. DMs in this module are always between distinct users, so including your own id yields a BadRequest CustomException. The check is an ordinal string comparison against the current user's id.
Solutions
- Filter the current user's id out of UserIds on the client before calling the API.
- Show a self-DM option only via the dedicated self-note flow if one exists, never through this endpoint.
- Server-side: keep this guard — optionally return a clearer 400 payload naming the offending id.
Example fix
// before
await api.post('/channels/dm', { userIds: [me.id, other.id] });
// after
const others = [...new Set(targets)].filter(id => id !== me.id);
if (others.length === 0) throw new Error('Cannot start a DM with yourself.');
await api.post('/channels/dm', { userIds: others }); Defensive patterns
Strategy: validation
Validate before calling
const others = [...new Set(userIds)].filter(id => id !== currentUserId);
if (others.length === 0) throw new Error('Cannot DM yourself.'); Type guard
const isSelfDm = (ids: string[], me: string) => ids.some(id => id === me);
Prevention
- Always filter the current user's id out of DM participant lists client-side.
- Disable the 'message' button on your own profile.
- Use a Set to dedupe ids before sending.
- Compare ids exactly as the server does (ordinal, same string form).
When it happens
Trigger: Calling POST find-or-create-DM (POST /channels/dm) with cmd.UserIds containing the authenticated user's own id string (exact ordinal match, e.g. the id returned by /users/me passed back verbatim).
Common situations: Frontend pre-selecting the current user in a 'new conversation' multi-select; a 'message user' button rendered on your own profile page; ids compared with different casing via case-insensitive comparisons elsewhere leading devs to think a duplicate won't match.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Cannot start a DM with yourself.
- no current user
- Parent message belongs to a different channel.
- Cannot reply to a reply — threads are single-level only.
- A category cannot be its own parent.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/35e203e80d7a84b1.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Channels/FindOrCreateDm/FindOrCreateDmCommandHandler.cs:30
namespace FSH.Modules.Chat.Features.v1.Channels.FindOrCreateDm;
public sealed class FindOrCreateDmCommandHandler(
ChatDbContext db,
IHubContext<AppHub> hub,
ICurrentUser currentUser)
: ICommandHandler<FindOrCreateDmCommand, Guid>
{
public async ValueTask<Guid> Handle(FindOrCreateDmCommand cmd, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(cmd);
var userId = currentUser.GetUserId();
if (userId == Guid.Empty) throw new UnauthorizedException("no current user");
var currentUserId = userId.ToString();
var otherIds = cmd.UserIds.Distinct(StringComparer.Ordinal).ToList();
if (otherIds.Any(id => string.Equals(id, currentUserId, StringComparison.Ordinal)))
{
throw new CustomException("Cannot DM yourself.", (IEnumerable<string>?)null, System.Net.HttpStatusCode.BadRequest);
}
if (otherIds.Count == 1)
{
// Two-person DM — deterministic lookup via DirectKey.
var (lo, hi) = string.CompareOrdinal(currentUserId, otherIds[0]) < 0
? (currentUserId, otherIds[0])
: (otherIds[0], currentUserId);
var directKey = $"{lo}:{hi}";
var existing = await db.Channels
.FirstOrDefaultAsync(c => c.Type == ChannelType.DirectMessage && c.DirectKey == directKey, cancellationToken)
.ConfigureAwait(false);
if (existing is not null) return existing.Id;
var dm = ChatChannel.CreateDirect(currentUserId, otherIds[0]);
db.Channels.Add(dm);
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);View on GitHub (pinned to 3f2959e683)