fullstackhero/dotnet-starter-kit · error · UnauthorizedException
no current user
Error message
no current user
What it means
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.
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
Defensive patterns
Strategy: try-catch
Validate before calling
if (!auth.isAuthenticated()) await auth.login();
Type guard
function userIdPresent(u: { Id: string } | null): u is { Id: string } { return u !== null && u.Id !== '00000000-0000-0000-0000-000000000000'; } Try / catch
try { return await listMessageReplies(parentId); }
catch (e) { if (isUnauthorized(e)) { await auth.refresh(); return listMessageReplies(parentId); } throw e; } Prevention
- Refresh tokens proactively
- Global interceptor adds Authorization header
- Seed user context in handler-level tests
- Log 401s client-side to spot token expiry patterns
When it happens
Trigger: Fetching message replies without a valid JWT or with an expired/invalid token.
Common situations: Expired access token not refreshed by the client, anonymous testing of the endpoint, or integration tests invoking the handler without a seeded current user.
Related errors
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/566742195f00fdde.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Messages/ListMessageReplies/ListMessageRepliesQueryHandler.cs:25
using FSH.Modules.Chat.Features.v1.Internal;
using Mediator;
using Microsoft.EntityFrameworkCore;
namespace FSH.Modules.Chat.Features.v1.Messages.ListMessageReplies;
public sealed class ListMessageRepliesQueryHandler(
ChatDbContext db,
ICurrentUser currentUser,
IMediator mediator)
: IQueryHandler<ListMessageRepliesQuery, ReadOnlyCollection<MessageDto>>
{
public async ValueTask<ReadOnlyCollection<MessageDto>> Handle(
ListMessageRepliesQuery query,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(query);
var userId = currentUser.GetUserId();
if (userId == Guid.Empty) throw new UnauthorizedException("no current user");
var currentUserId = userId.ToString();
// Load the parent so we can authorize the caller through the channel.
var parent = await db.Messages
.Where(m => m.Id == query.ParentMessageId)
.Select(m => new { m.Id, m.ChannelId })
.FirstOrDefaultAsync(cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException("Parent message not found.");
var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == parent.ChannelId, cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException("Parent message not found.");
channel.RequireMember(currentUserId);
IQueryable<Domain.Message> q = db.Messages
.Where(m => m.ParentMessageId == query.ParentMessageId);
View on GitHub (pinned to 3f2959e683)