fullstackhero/dotnet-starter-kit · error · UnauthorizedException

no current user

Error message

no current user

What it means

ArchiveChannelCommandHandler throws UnauthorizedException when currentUser.GetUserId() returns Guid.Empty, i.e. no authenticated user is on the request. Archiving requires a caller both for the RequireAdmin check and audit attribution.

Solutions

  1. Send a valid non-expired bearer token with the request
  2. Handle 401 at the client by refreshing/re-authenticating
  3. In tests, mock ICurrentUser.GetUserId() to return a real Guid

Example fix

// before
currentUser.GetUserId().Returns(Guid.Empty);
// after
currentUser.GetUserId().Returns(Guid.NewGuid());
Defensive patterns

Strategy: validation

Validate before calling

var userId = currentUser.GetUserId();
if (userId == Guid.Empty) throw new UnauthorizedException("no current user");

Type guard

bool isAuthenticated = currentUser.GetUserId() != Guid.Empty;

Try / catch

try { await handler.Handle(cmd, ct); }
catch (UnauthorizedException) { /* return 401 / re-authenticate */ }

Prevention

When it happens

Trigger: Calling the ArchiveChannel endpoint without a valid JWT, with an expired token or anonymous identity, or invoking the handler in tests without a mocked ICurrentUser.

Common situations: Client kept calling after token expiry; missing Authorization header; integration test forgot to authenticate the HttpClient.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/da42844a292220bc. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Channels/ArchiveChannel/ArchiveChannelCommandHandler.cs:20

using FSH.Framework.Core.Exceptions;
using FSH.Modules.Chat.Contracts.v1.Commands;
using FSH.Modules.Chat.Data;
using FSH.Modules.Chat.Features.v1.Internal;
using Mediator;
using Microsoft.EntityFrameworkCore;

namespace FSH.Modules.Chat.Features.v1.Channels.ArchiveChannel;

public sealed class ArchiveChannelCommandHandler(
    ChatDbContext db,
    ICurrentUser currentUser)
    : ICommandHandler<ArchiveChannelCommand, Unit>
{
    public async ValueTask<Unit> Handle(ArchiveChannelCommand cmd, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(cmd);
        var userId = currentUser.GetUserId();
        if (userId == Guid.Empty) throw new UnauthorizedException("no current user");

        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == cmd.ChannelId, cancellationToken)
            .ConfigureAwait(false)
            ?? throw new NotFoundException("Channel not found.");

        channel.RequireAdmin(userId.ToString());

        // Explicit soft-delete (not db.Remove): removing the aggregate cascades Deleted onto
        // the ChannelMember rows, which the audit interceptor does not rescue (they're FK
        // children, not owned), so they'd be hard-deleted and lost on restore.
        channel.Archive(userId.ToString());
        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        return Unit.Value;
    }
}

View on GitHub (pinned to 3f2959e683)