fullstackhero/dotnet-starter-kit · error · UnauthorizedException
no current user
Error message
no current user
What it means
MarkChannelReadCommandHandler requires a resolved current user; Guid.Empty from currentUser.GetUserId() triggers UnauthorizedException before any channel lookup. Read-state updates are per-user, so an identity is mandatory.
Solutions
- Ensure a valid access token is attached to every read-marker request.
- Refresh expired tokens proactively before emitting read receipts.
- Wire a global 401 interceptor that re-authenticates and replays the request.
- Check JWT claim configuration if tokens are valid but claims are unmapped.
Example fix
// before
api.post(`/channels/${id}/read`, { messageId }).catch(() => {});
// after
await withAuth(() => api.post(`/channels/${id}/read`, { messageId })); Defensive patterns
Strategy: try-catch
Validate before calling
if (!accessToken || isTokenExpired(accessToken)) await refreshToken();
Try / catch
try { ... } catch (e) {
if (isUnauthorized(e)) { await reauthenticate(); replayMarkRead(); }
else if (isNotFound(e)) { /* drop marker */ }
else throw e;
} Prevention
- Never fire read receipts from a bare fetch without the auth header.
- Refresh tokens on window focus for background-tab badge updates.
- Queue read markers and replay after re-auth instead of dropping them.
- Centralize 401 handling in one interceptor.
When it happens
Trigger: Calling the mark-channel-read command (POST /channels/{channelId}/read) with no/invalid/expired JWT, so the handler can't attribute the read marker to a user.
Common situations: SignalR-style badge clearing fired from a background tab whose token expired; auth header omitted from a fire-and-forget fetch; identity not yet hydrated on app resume.
Related errors
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/900e899e077f6f1f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Channels/MarkChannelRead/MarkChannelReadCommandHandler.cs:23
using FSH.Modules.Chat.Data;
using FSH.Modules.Chat.Features.v1.Internal;
using Mediator;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
namespace FSH.Modules.Chat.Features.v1.Channels.MarkChannelRead;
public sealed class MarkChannelReadCommandHandler(
ChatDbContext db,
ICurrentUser currentUser,
IHubContext<AppHub> hub)
: ICommandHandler<MarkChannelReadCommand, Unit>
{
public async ValueTask<Unit> Handle(MarkChannelReadCommand cmd, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(cmd);
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 == cmd.ChannelId, cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException("Channel not found.");
channel.RequireMember(currentUserId);
// Verify the marker message actually exists in this channel.
var exists = await db.Messages
.AnyAsync(m => m.Id == cmd.MessageId && m.ChannelId == cmd.ChannelId, cancellationToken)
.ConfigureAwait(false);
if (!exists) throw new NotFoundException("Message not found in this channel.");
channel.MarkRead(currentUserId, cmd.MessageId);
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
// Push to the user's other tabs so the badge clears everywhere at once.
await hub.Clients.Group($"user:{currentUserId}")View on GitHub (pinned to 3f2959e683)