{"record":{"id":"35e203e80d7a84b1","repo":"fullstackhero/dotnet-starter-kit","slug":"cannot-dm-yourself","errorCode":null,"errorMessage":"Cannot DM yourself.","messagePattern":"Cannot DM yourself\\.","errorType":"exception","errorClass":"CustomException","httpStatus":400,"severity":"warning","filePath":"src/Modules/Chat/Modules.Chat/Features/v1/Channels/FindOrCreateDm/FindOrCreateDmCommandHandler.cs","lineNumber":30,"sourceCode":"namespace FSH.Modules.Chat.Features.v1.Channels.FindOrCreateDm;\n\npublic sealed class FindOrCreateDmCommandHandler(\n    ChatDbContext db,\n    IHubContext<AppHub> hub,\n    ICurrentUser currentUser)\n    : ICommandHandler<FindOrCreateDmCommand, Guid>\n{\n    public async ValueTask<Guid> Handle(FindOrCreateDmCommand cmd, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(cmd);\n        var userId = currentUser.GetUserId();\n        if (userId == Guid.Empty) throw new UnauthorizedException(\"no current user\");\n        var currentUserId = userId.ToString();\n\n        var otherIds = cmd.UserIds.Distinct(StringComparer.Ordinal).ToList();\n        if (otherIds.Any(id => string.Equals(id, currentUserId, StringComparison.Ordinal)))\n        {\n            throw new CustomException(\"Cannot DM yourself.\", (IEnumerable<string>?)null, System.Net.HttpStatusCode.BadRequest);\n        }\n\n        if (otherIds.Count == 1)\n        {\n            // Two-person DM — deterministic lookup via DirectKey.\n            var (lo, hi) = string.CompareOrdinal(currentUserId, otherIds[0]) < 0\n                ? (currentUserId, otherIds[0])\n                : (otherIds[0], currentUserId);\n            var directKey = $\"{lo}:{hi}\";\n\n            var existing = await db.Channels\n                .FirstOrDefaultAsync(c => c.Type == ChannelType.DirectMessage && c.DirectKey == directKey, cancellationToken)\n                .ConfigureAwait(false);\n            if (existing is not null) return existing.Id;\n\n            var dm = ChatChannel.CreateDirect(currentUserId, otherIds[0]);\n            db.Channels.Add(dm);\n            await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Features/v1/Channels/FindOrCreateDm/FindOrCreateDmCommandHandler.cs#L12-L48","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nawait api.post('/channels/dm', { userIds: [me.id, other.id] });\n// after\nconst others = [...new Set(targets)].filter(id => id !== me.id);\nif (others.length === 0) throw new Error('Cannot start a DM with yourself.');\nawait api.post('/channels/dm', { userIds: others });","handlingStrategy":"validation","validationCode":"const others = [...new Set(userIds)].filter(id => id !== currentUserId);\nif (others.length === 0) throw new Error('Cannot DM yourself.');","typeGuard":"const isSelfDm = (ids: string[], me: string) => ids.some(id => id === me);","tryCatchPattern":null,"preventionTips":["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)."],"tags":["chat","dm","bad-request"],"backgroundTag":"invalid-argument-value","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"}