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
- Gate the initial channels fetch behind completed authentication (await token readiness).
- Refresh the access token and retry the request.
- Inspect the JWT claims and the host's bearer/token validation settings.
- 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
- Gate the channels query behind an isAuthenticated flag (TanStack Query 'enabled').
- Wait for auth bootstrap before firing initial data loads.
- Handle 401 globally with token refresh + replay.
- Test endpoints with a fresh token; expired tokens fail even for valid users.
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)