fullstackhero/dotnet-starter-kit · error · UnauthorizedException

no current user

Error message

no current user

What it means

ListChannelMessages throws UnauthorizedException('no current user') when currentUser.GetUserId() returns Guid.Empty, i.e. no authenticated identity on the request. Maps to HTTP 401 and is thrown before the channel lookup to avoid leaking channel existence to anonymous callers.

Solutions

  1. Obtain/refresh a valid access token and send it in the Authorization header
  2. Fix token refresh logic so expired tokens are renewed before calls
  3. Ensure test harnesses/background jobs seed an ICurrentUser instead of calling handlers raw
  4. Validate JWT bearer options (issuer/audience) so tokens authenticate
Defensive patterns

Strategy: try-catch

Validate before calling

if (!auth.isAuthenticated()) throw new Error('login required before listing messages');

Type guard

function isAuthed(ctx: { UserId?: string }): boolean { return typeof ctx.UserId === 'string' && ctx.UserId !== '00000000-0000-0000-0000-000000000000'; }

Try / catch

try { return await listChannelMessages(channelId); }
catch (e) { if (isUnauthorized(e)) { await auth.refresh(); return listChannelMessages(channelId); } throw e; }

Prevention

When it happens

Trigger: Listing channel messages without a valid JWT; missing, expired, or otherwise unauthenticated request context.

Common situations: SignalR/SSE or background contexts calling the handler without user propagation, expired tokens, or misconfigured auth middleware in tests.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/928641cbc51c0d55. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Messages/ListChannelMessages/ListChannelMessagesQueryHandler.cs:25

using FSH.Modules.Chat.Features.v1.Internal;
using Mediator;
using Microsoft.EntityFrameworkCore;

namespace FSH.Modules.Chat.Features.v1.Messages.ListChannelMessages;

public sealed class ListChannelMessagesQueryHandler(
    ChatDbContext db,
    ICurrentUser currentUser,
    IMediator mediator)
    : IQueryHandler<ListChannelMessagesQuery, ReadOnlyCollection<MessageDto>>
{
    public async ValueTask<ReadOnlyCollection<MessageDto>> Handle(
        ListChannelMessagesQuery query,
        CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(query);
        var userId = currentUser.GetUserId();
        if (userId == Guid.Empty) throw new UnauthorizedException("no current user");
        var currentUserId = userId.ToString();

        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == query.ChannelId, cancellationToken)
            .ConfigureAwait(false)
            ?? throw new NotFoundException("Channel not found.");
        channel.RequireMember(currentUserId);

        // Top-level only (no thread replies). Guid v7 monotonic → Id desc = time desc.
        IQueryable<Domain.Message> q = db.Messages
            .Where(m => m.ChannelId == query.ChannelId && m.ParentMessageId == null);

        if (query.Before is { } beforeId)
        {
            q = q.Where(m => m.Id.CompareTo(beforeId) < 0);
        }

        var rows = await q
            .OrderByDescending(m => m.Id)

View on GitHub (pinned to 3f2959e683)