fullstackhero/dotnet-starter-kit · error · UnauthorizedException
no current user
Error message
no current user
What it means
DeleteMessageCommandHandler throws UnauthorizedException("no current user") when currentUser.GetUserId() returns Guid.Empty, i.e. the request carries no authenticated user identity. The guard runs before any database access so deletes are never attempted anonymously. Rendered as HTTP 401 by the module's exception handling.
Solutions
- Send a valid, unexpired bearer token with the request.
- Ensure the endpoint/hub method requires authentication and auth middleware runs before handlers.
- Implement token refresh on 401 in the frontend apiFetch layer.
- In tests/background jobs, provide a mocked ICurrentUser returning a concrete Guid.
Example fix
// before
var response = await fetch(`/api/v1/messages/${id}`, { method: "DELETE" }); // 401
// after
apiFetch(`/api/v1/messages/${id}`, { method: "DELETE" }); // attaches bearer token + refreshes Defensive patterns
Strategy: try-catch
Validate before calling
if (!auth.accessToken || isExpired(auth.accessToken)) {
await refreshAccessToken(); // authenticate before deleting
} Type guard
function hasIdentity(u: { id?: string }): u is { id: string } {
return typeof u.id === "string" && u.id !== "" && u.id !== "00000000-0000-0000-0000-000000000000";
} Try / catch
try {
await api.deleteMessage(messageId);
} catch (e) {
if (e.status === 401) { await refreshAndRetry(); }
else throw e;
} Prevention
- Route all API calls through an authenticated fetch wrapper.
- Handle 401 globally with a token refresh + retry.
- Disable destructive actions until auth state is resolved.
- Keep [Authorize] on delete endpoints.
When it happens
Trigger: Calling the delete-message endpoint without a valid JWT; invoking the handler from a Hangfire job, SignalR invocation, or test where no ICurrentUser is established.
Common situations: Expired access token not refreshed by the frontend; missing [Authorize] on the endpoint letting anonymous calls through to the handler; SignalR hub methods invoked before negotiation completes authentication; test doubles returning default(Guid).
Related errors
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/342da2d89bfe025a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Messages/DeleteMessage/DeleteMessageCommandHandler.cs:26
using FSH.Modules.Identity.Contracts.Services;
using Mediator;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
namespace FSH.Modules.Chat.Features.v1.Messages.DeleteMessage;
public sealed class DeleteMessageCommandHandler(
ChatDbContext db,
ICurrentUser currentUser,
IUserPermissionService permissions,
IHubContext<AppHub> hub)
: ICommandHandler<DeleteMessageCommand, Unit>
{
public async ValueTask<Unit> Handle(DeleteMessageCommand 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 message = await db.Messages.FirstOrDefaultAsync(m => m.Id == cmd.MessageId, cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException("Message not found.");
var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == message.ChannelId, cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException("Message not found.");
channel.RequireMember(currentUserId);
bool isModerator = await permissions
.HasPermissionAsync(currentUserId, ChatPermissions.Messages.DeleteAny, cancellationToken)
.ConfigureAwait(false);
message.SoftDelete(currentUserId, isModerator);
// If this was a thread reply, decrement the parent's ReplyCount.View on GitHub (pinned to 3f2959e683)