{"record":{"id":"7bae0bfc9b8d8973","repo":"fullstackhero/dotnet-starter-kit","slug":"no-current-user-pinmessagecommandhandler","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/Messages/PinMessage/PinMessageCommandHandler.cs","lineNumber":23,"sourceCode":"using FSH.Modules.Chat.Data;\nusing FSH.Modules.Chat.Features.v1.Internal;\nusing Mediator;\nusing Microsoft.AspNetCore.SignalR;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Chat.Features.v1.Messages.PinMessage;\n\npublic sealed class PinMessageCommandHandler(\n    ChatDbContext db,\n    ICurrentUser currentUser,\n    IHubContext<AppHub> hub)\n    : ICommandHandler<PinMessageCommand, Unit>\n{\n    public async ValueTask<Unit> Handle(PinMessageCommand 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 message = await db.Messages.FirstOrDefaultAsync(m => m.Id == cmd.MessageId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Message not found.\");\n\n        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == message.ChannelId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Message not found.\");\n        channel.RequireMember(currentUserId);\n\n        message.Pin(currentUserId);\n        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n\n        await hub.Clients.Group($\"channel:{channel.Id}\")\n            .SendAsync(\"ChatMessagePinned\", message.ToDto(), cancellationToken)\n            .ConfigureAwait(false);\n        return Unit.Value;","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Features/v1/Messages/PinMessage/PinMessageCommandHandler.cs#L5-L41","documentation":"PinMessageCommandHandler throws UnauthorizedException('no current user') when currentUser.GetUserId() returns Guid.Empty, meaning the request carried no authenticated identity. This is the library's 401 path, thrown before any message lookup.","triggerScenarios":"Pinning a message without a valid JWT; missing/expired/invalid bearer token.","commonSituations":"Expired token not refreshed in the client, raw handler invocation in tests without a current user, or auth middleware misconfiguration.","solutions":["Refresh/acquire a valid access token and send it with the request","Verify JWT bearer options and that authentication runs before authorization","Seed an ICurrentUser in test harnesses calling handlers directly","Confirm the client attaches the Authorization header on mutation calls"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (!auth.isAuthenticated()) await auth.login();","typeGuard":"function canPin(user: { Id: string } | null): boolean { return user !== null && user.Id !== '00000000-0000-0000-0000-000000000000'; }","tryCatchPattern":"try { await pinMessage(messageId); }\ncatch (e) { if (isUnauthorized(e)) { await auth.refresh(); return pinMessage(messageId); } throw e; }","preventionTips":["Disable pin actions when logged out","Refresh token before mutations","Send Authorization header on every mutation","Seed ICurrentUser in handler tests"],"tags":["auth","chat","jwt"],"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"}