fullstackhero/dotnet-starter-kit · error · UnauthorizedException
no current user
Error message
no current user
What it means
The GetPinnedMessages handler calls currentUser.GetUserId() and throws UnauthorizedException when the resolved user id is Guid.Empty, meaning no authenticated user is present on the request context. The FSH UnauthorizedException maps to HTTP 401. It is thrown before any database work so unauthenticated callers learn nothing about the channel.
Solutions
- Log in / refresh the access token and retry with a valid Authorization: Bearer header
- Verify the JWT bearer configuration (authority, audience, signing key) matches the token issuer
- Check the client actually attaches the Authorization header (proxy/interceptor stripping it)
- Confirm the endpoint's authentication middleware runs before the endpoint handler
Example fix
// before
var res = await apiFetch('/api/v1/channels/'+id+'/messages/pinned');
// after
if (!auth.hasToken()) await auth.refresh();
var res = await apiFetch('/api/v1/channels/'+id+'/messages/pinned', { auth: true }); Defensive patterns
Strategy: try-catch
Validate before calling
if (!auth.isAuthenticated()) await auth.login();
Type guard
function hasUser(u: { Id: string }): boolean { return !!u.Id && u.Id !== '00000000-0000-0000-0000-000000000000'; } Try / catch
try { return await getPinnedMessages(channelId); }
catch (e) { if (isUnauthorized(e)) { await auth.refresh(); return getPinnedMessages(channelId); } throw e; } Prevention
- Always refresh expired tokens before API calls
- Attach the Authorization header via a central fetch interceptor
- Redirect to login on 401 instead of retrying raw
- Keep JWT bearer config (issuer/audience) in sync with the identity provider
When it happens
Trigger: Calling GET pinned messages without a valid JWT; the token was absent, expired, or failed authentication so ICurrentUser returns Guid.Empty.
Common situations: Frontend calls the endpoint before login, an expired access token was not refreshed, or the JWT bearer middleware was misconfigured (bad issuer/audience/signing key) so the user is never populated.
Related errors
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/00dd705f44445985.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Messages/GetPinnedMessages/GetPinnedMessagesQueryHandler.cs:25
using FSH.Modules.Chat.Features.v1.Internal;
using Mediator;
using Microsoft.EntityFrameworkCore;
namespace FSH.Modules.Chat.Features.v1.Messages.GetPinnedMessages;
public sealed class GetPinnedMessagesQueryHandler(
ChatDbContext db,
ICurrentUser currentUser,
IMediator mediator)
: IQueryHandler<GetPinnedMessagesQuery, ReadOnlyCollection<MessageDto>>
{
public async ValueTask<ReadOnlyCollection<MessageDto>> Handle(
GetPinnedMessagesQuery 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);
var rows = await db.Messages
.Where(m => m.ChannelId == query.ChannelId && m.IsPinned)
.OrderByDescending(m => m.PinnedAtUtc)
.Include(m => m.Attachments)
.Include(m => m.Mentions)
.AsNoTracking()
.ToListAsync(cancellationToken)
.ConfigureAwait(false);
var dtos = rows.Select(m => m.ToDto()).ToList();
var resolved = await ChatAttachmentUrls.ResolveAsync(dtos, mediator, cancellationToken).ConfigureAwait(false);View on GitHub (pinned to 3f2959e683)