fullstackhero/dotnet-starter-kit · error · UnauthorizedException

no current user

Error message

no current user

What it means

ListMyFilesQueryHandler calls currentUser.GetUserId() and rejects the result when it is null/empty or Guid.Empty, throwing UnauthorizedException ('no current user', HTTP 401). The list endpoint is inherently per-user, so an unauthenticated or anonymous principal cannot be served.

Solutions

  1. Attach a valid, unexpired JWT Bearer token to the request
  2. Re-authenticate (refresh/login) if the token expired
  3. Ensure the token contains the user id claim expected by GetUserId()
  4. Don't call this endpoint from non-user contexts — use a user-scoped token or an admin query path

Example fix

// before
const files = await apiFetch('/api/v1/files/mine'); // no auth header
// after
const files = await apiFetch('/api/v1/files/mine', {
  headers: { Authorization: `Bearer ${await getToken()}` }
});
Defensive patterns

Strategy: try-catch

Validate before calling

const token = await getAccessToken();
if (!token) { redirectToLogin(); }

Type guard

const isAuthenticated = (u: User | null): u is User => u !== null && /^[0-9a-f-]{36}$/i.test(u.id) && u.id !== '00000000-0000-0000-0000-000000000000';

Try / catch

try { const files = await api.listMyFiles(); }
catch (e) { if (e.status === 401) { await reauthenticate(); return retry(); } throw e; }

Prevention

When it happens

Trigger: Calling the list-my-files endpoint without a valid JWT; an authenticated principal whose id claim is missing/unparseable; anonymous access where the current-user accessor returns Guid.Empty.

Common situations: Expired or missing Bearer token in the client; calling the endpoint from a background job or service without user impersonation; auth middleware misconfigured so claims aren't populated; token issued without the user id claim.

Related errors


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

Appendix: source

Thrown at src/Modules/Files/Modules.Files/Features/v1/ListMyFiles/ListMyFilesQueryHandler.cs:27

using FSH.Modules.Files.Features.v1.Internal;
using Mediator;
using Microsoft.EntityFrameworkCore;

namespace FSH.Modules.Files.Features.v1.ListMyFiles;

public sealed class ListMyFilesQueryHandler(
    FilesDbContext db,
    ICurrentUser currentUser,
    IStorageService storage)
    : IQueryHandler<ListMyFilesQuery, ReadOnlyCollection<FileAssetDto>>
{
    public async ValueTask<ReadOnlyCollection<FileAssetDto>> Handle(ListMyFilesQuery q, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(q);
        var userId = currentUser.GetUserId().ToString();
        if (string.IsNullOrEmpty(userId) || userId == Guid.Empty.ToString())
        {
            throw new UnauthorizedException("no current user");
        }

        var page = Math.Max(1, q.Page);
        var pageSize = Math.Clamp(q.PageSize, 1, 100);

        var rows = await db.FileAssets.AsNoTracking()
            .Where(f => f.CreatedByUserId == userId && f.Status == FileAssetStatus.Available)
            .OrderByDescending(f => f.CreatedAtUtc)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync(cancellationToken)
            .ConfigureAwait(false);

        // Seed publicUrl for public files so the preview dialog can paint the image
        // immediately from the list data, without waiting on a metadata refetch to mint it.
        return rows
            .Select(f => FileAssetMapper.ToDto(
                f,

View on GitHub (pinned to 3f2959e683)