{"record":{"id":"16a2e0d18a1fee5b","repo":"fullstackhero/dotnet-starter-kit","slug":"no-current-user-findorcreatedmcommandhandler","errorCode":null,"errorMessage":"no current user","messagePattern":"no current user","errorType":"exception","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"src/Modules/Chat/Modules.Chat/Features/v1/Channels/FindOrCreateDm/FindOrCreateDmCommandHandler.cs","lineNumber":24,"sourceCode":"using FSH.Modules.Chat.Data;\nusing FSH.Modules.Chat.Domain;\nusing Mediator;\nusing Microsoft.AspNetCore.SignalR;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace 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)","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Features/v1/Channels/FindOrCreateDm/FindOrCreateDmCommandHandler.cs#L6-L42","documentation":"FindOrCreateDmCommandHandler throws UnauthorizedException when currentUser.GetUserId() returns Guid.Empty, i.e. no authenticated caller. The DM channel is created between the current user and the given users, so identity is required before any other validation.","triggerScenarios":"Calling FindOrCreateDm without a valid JWT, with an expired token, or in tests without a configured ICurrentUser. Note: passing your own ID in UserIds instead raises 'Cannot DM yourself.' (CustomException), not this error.","commonSituations":"Session expired before opening a DM; missing Authorization header; integration tests without auth setup.","solutions":["Authenticate the request with a valid bearer token","Handle 401 by re-authenticating/refreshing before retrying","In tests, mock ICurrentUser.GetUserId() to return a real Guid"],"exampleFix":"// before\ncurrentUser.GetUserId().Returns(Guid.Empty); // handler throws\n// after\ncurrentUser.GetUserId().Returns(Guid.NewGuid());","handlingStrategy":"validation","validationCode":"var userId = currentUser.GetUserId();\nif (userId == Guid.Empty) throw new UnauthorizedException(\"no current user\");","typeGuard":"bool isAuthenticated = currentUser.GetUserId() != Guid.Empty;","tryCatchPattern":"try { await findOrCreateDm(userIds); }\ncatch (UnauthorizedException) { /* re-authenticate */ }\ncatch (CustomException) { /* 'Cannot DM yourself.' — filter out own id */ }","preventionTips":["Attach JWT to DM requests","Filter the caller's own ID from UserIds before submit (avoids the related 'Cannot DM yourself.' error)","Stub ICurrentUser with a real Guid in tests"],"tags":["auth","unauthorized","chat","dm"],"backgroundTag":"authentication-required","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"}