fullstackhero/dotnet-starter-kit · error · UnauthorizedException

no current user

Error message

no current user

What it means

ListMyChannelsQueryHandler requires an authenticated user; when currentUser.GetUserId() returns Guid.Empty (no valid identity on the request), it throws UnauthorizedException before querying. This is an auth-scope error, not a data error.

Solutions

  1. Gate the initial channels fetch behind completed authentication (await token readiness).
  2. Refresh the access token and retry the request.
  3. Inspect the JWT claims and the host's bearer/token validation settings.
  4. Confirm the Authorization header survives any reverse proxy.

Example fix

// before
useQuery({ queryKey: ['channels'], queryFn: () => api.get('/channels/my') });
// after
useQuery({
  queryKey: ['channels'],
  queryFn: () => api.get('/channels/my'),
  enabled: isAuthenticated
});
Defensive patterns

Strategy: try-catch

Validate before calling

if (!accessToken) throw new Error('Not authenticated');

Try / catch

try { ... } catch (e) {
  if (isUnauthorized(e)) { redirectToLogin(); }
  else throw e;
}

Prevention

When it happens

Trigger: Calling GET /channels/my (paginated list of my channels) without a valid JWT, with an expired token, or in any context where the current-user accessor cannot resolve a user id.

Common situations: App boots and fetches channels before auth bootstrap completes; token refresh failed silently; Authorization header dropped by an intercepting proxy; testing the endpoint with curl without a token.

Related errors


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

Appendix: source

Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Channels/ListMyChannels/ListMyChannelsQueryHandler.cs:22

using FSH.Modules.Chat.Contracts.v1.DTOs;
using FSH.Modules.Chat.Contracts.v1.Queries;
using FSH.Modules.Chat.Data;
using FSH.Modules.Chat.Features.v1.Internal;
using Mediator;
using Microsoft.EntityFrameworkCore;

namespace FSH.Modules.Chat.Features.v1.Channels.ListMyChannels;

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

        int page = Math.Max(1, q.Page);
        int pageSize = Math.Clamp(q.PageSize, 1, 200);

        var channels = await db.Channels.AsNoTracking()
            .Where(c => c.Members.Any(m => m.UserId == currentUserId))
            .OrderByDescending(c => c.LastMessageAtUtc ?? c.CreatedAtUtc)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync(cancellationToken)
            .ConfigureAwait(false);

        // Single round-trip: count each channel's unread messages via a correlated subquery instead of
        // one CountAsync per channel (was N+1, up to 200 round-trips).
        var channelIds = channels.Select(c => c.Id).ToList();
        var unread = await db.Channels.AsNoTracking()
            .Where(c => channelIds.Contains(c.Id))

View on GitHub (pinned to 3f2959e683)