{"record":{"id":"566742195f00fdde","repo":"fullstackhero/dotnet-starter-kit","slug":"no-current-user-listmessagerepliesqueryhandler","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/ListMessageReplies/ListMessageRepliesQueryHandler.cs","lineNumber":25,"sourceCode":"using FSH.Modules.Chat.Features.v1.Internal;\nusing Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Chat.Features.v1.Messages.ListMessageReplies;\n\npublic sealed class ListMessageRepliesQueryHandler(\n    ChatDbContext db,\n    ICurrentUser currentUser,\n    IMediator mediator)\n    : IQueryHandler<ListMessageRepliesQuery, ReadOnlyCollection<MessageDto>>\n{\n    public async ValueTask<ReadOnlyCollection<MessageDto>> Handle(\n        ListMessageRepliesQuery 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        // Load the parent so we can authorize the caller through the channel.\n        var parent = await db.Messages\n            .Where(m => m.Id == query.ParentMessageId)\n            .Select(m => new { m.Id, m.ChannelId })\n            .FirstOrDefaultAsync(cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Parent message not found.\");\n\n        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == parent.ChannelId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Parent message not found.\");\n        channel.RequireMember(currentUserId);\n\n        IQueryable<Domain.Message> q = db.Messages\n            .Where(m => m.ParentMessageId == query.ParentMessageId);\n","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Features/v1/Messages/ListMessageReplies/ListMessageRepliesQueryHandler.cs#L7-L43","documentation":"ListMessageReplies throws UnauthorizedException('no current user') when the request context has no authenticated user (GetUserId() == Guid.Empty). This is the library's 401 signal, thrown before loading the parent message.","triggerScenarios":"Fetching message replies without a valid JWT or with an expired/invalid token.","commonSituations":"Expired access token not refreshed by the client, anonymous testing of the endpoint, or integration tests invoking the handler without a seeded current user.","solutions":["Send a valid Authorization: Bearer token; refresh it if expired","Ensure the auth middleware authenticates the request before the endpoint runs","In tests/background contexts, seed a current user or use an authenticated client","Check client interceptors attach the header on every request"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (!auth.isAuthenticated()) await auth.login();","typeGuard":"function userIdPresent(u: { Id: string } | null): u is { Id: string } { return u !== null && u.Id !== '00000000-0000-0000-0000-000000000000'; }","tryCatchPattern":"try { return await listMessageReplies(parentId); }\ncatch (e) { if (isUnauthorized(e)) { await auth.refresh(); return listMessageReplies(parentId); } throw e; }","preventionTips":["Refresh tokens proactively","Global interceptor adds Authorization header","Seed user context in handler-level tests","Log 401s client-side to spot token expiry patterns"],"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"}