{"record":{"id":"00dd705f44445985","repo":"fullstackhero/dotnet-starter-kit","slug":"no-current-user-getpinnedmessagesqueryhandler","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/GetPinnedMessages/GetPinnedMessagesQueryHandler.cs","lineNumber":25,"sourceCode":"using FSH.Modules.Chat.Features.v1.Internal;\nusing Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Chat.Features.v1.Messages.GetPinnedMessages;\n\npublic sealed class GetPinnedMessagesQueryHandler(\n    ChatDbContext db,\n    ICurrentUser currentUser,\n    IMediator mediator)\n    : IQueryHandler<GetPinnedMessagesQuery, ReadOnlyCollection<MessageDto>>\n{\n    public async ValueTask<ReadOnlyCollection<MessageDto>> Handle(\n        GetPinnedMessagesQuery query,\n        CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(query);\n        var userId = currentUser.GetUserId();\n        if (userId == Guid.Empty) throw new UnauthorizedException(\"no current user\");\n        var currentUserId = userId.ToString();\n\n        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == query.ChannelId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Channel not found.\");\n        channel.RequireMember(currentUserId);\n\n        var rows = await db.Messages\n            .Where(m => m.ChannelId == query.ChannelId && m.IsPinned)\n            .OrderByDescending(m => m.PinnedAtUtc)\n            .Include(m => m.Attachments)\n            .Include(m => m.Mentions)\n            .AsNoTracking()\n            .ToListAsync(cancellationToken)\n            .ConfigureAwait(false);\n\n        var dtos = rows.Select(m => m.ToDto()).ToList();\n        var resolved = await ChatAttachmentUrls.ResolveAsync(dtos, mediator, cancellationToken).ConfigureAwait(false);","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Features/v1/Messages/GetPinnedMessages/GetPinnedMessagesQueryHandler.cs#L7-L43","documentation":"The GetPinnedMessages handler calls currentUser.GetUserId() and throws UnauthorizedException when the resolved user id is Guid.Empty, meaning no authenticated user is present on the request context. The FSH UnauthorizedException maps to HTTP 401. It is thrown before any database work so unauthenticated callers learn nothing about the channel.","triggerScenarios":"Calling GET pinned messages without a valid JWT; the token was absent, expired, or failed authentication so ICurrentUser returns Guid.Empty.","commonSituations":"Frontend calls the endpoint before login, an expired access token was not refreshed, or the JWT bearer middleware was misconfigured (bad issuer/audience/signing key) so the user is never populated.","solutions":["Log in / refresh the access token and retry with a valid Authorization: Bearer header","Verify the JWT bearer configuration (authority, audience, signing key) matches the token issuer","Check the client actually attaches the Authorization header (proxy/interceptor stripping it)","Confirm the endpoint's authentication middleware runs before the endpoint handler"],"exampleFix":"// before\nvar res = await apiFetch('/api/v1/channels/'+id+'/messages/pinned');\n// after\nif (!auth.hasToken()) await auth.refresh();\nvar res = await apiFetch('/api/v1/channels/'+id+'/messages/pinned', { auth: true });","handlingStrategy":"try-catch","validationCode":"if (!auth.isAuthenticated()) await auth.login();","typeGuard":"function hasUser(u: { Id: string }): boolean { return !!u.Id && u.Id !== '00000000-0000-0000-0000-000000000000'; }","tryCatchPattern":"try { return await getPinnedMessages(channelId); }\ncatch (e) { if (isUnauthorized(e)) { await auth.refresh(); return getPinnedMessages(channelId); } throw e; }","preventionTips":["Always refresh expired tokens before API calls","Attach the Authorization header via a central fetch interceptor","Redirect to login on 401 instead of retrying raw","Keep JWT bearer config (issuer/audience) in sync with the identity provider"],"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"}