fullstackhero/dotnet-starter-kit · error · NotFoundException

file not found

Error message

file not found

What it means

ChangeFileVisibilityCommandHandler looks up the FileAsset by cmd.FileAssetId via FirstOrDefaultAsync and throws NotFoundException when no row matches. This is a domain-level 'record not found' signal: the id supplied does not correspond to any existing file asset in the tenant-scoped query (soft-deleted or other-tenant rows are filtered out by BaseDbContext query filters).

Solutions

  1. Verify the FileAssetId exists and is not soft-deleted: query FileAssets ignoring filters, e.g. db.FileAssets.IgnoreQueryFilters().AnyAsync(x => x.Id == id).
  2. Check the request is sent under the correct tenant (tenant header/route) so the global query filter does not hide the row.
  3. Refresh the client-side file list; remove stale cached ids after delete operations.
  4. If the file should exist, confirm the DbMigrator/seed ran and you are pointed at the expected database.

Example fix

// before
await client.ChangeVisibilityAsync(new ChangeFileVisibilityCommand { FileAssetId = staleId, Visibility = Visibility.Public });
// after
var exists = await db.FileAssets.AnyAsync(x => x.Id == id);
if (!exists) { /* re-fetch fresh id from list endpoint or surface 404 to user */ }
await client.ChangeVisibilityAsync(new ChangeFileVisibilityCommand { FileAssetId = freshId, Visibility = Visibility.Public });
Defensive patterns

Strategy: validation

Validate before calling

var exists = await db.FileAssets.AnyAsync(x => x.Id == cmd.FileAssetId, ct);
if (!exists) throw new NotFoundException($"FileAsset {cmd.FileAssetId} does not exist in the current tenant.");

Type guard

bool IsValidFileAssetId(Guid id) => id != Guid.Empty;

Try / catch

catch (NotFoundException e) when (e.Message == "file not found") { return Results.NotFound(new { fileAssetId = cmd.FileAssetId }); }

Prevention

When it happens

Trigger: Calling the change-visibility endpoint/command with a FileAssetId that (a) never existed, (b) was already soft-deleted (IsDeleted filtered out), (c) belongs to a different tenant (tenant query filter), or (d) has a mistyped/stale GUID from a cached client reference.

Common situations: Frontend kept a stale file id after the file was deleted in another tab; test fixtures referencing seeded data from a different tenant; copying an id from logs of another environment; id truncation or casing mistakes when hand-crafting requests.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at src/Modules/Files/Modules.Files/Features/v1/ChangeVisibility/ChangeFileVisibilityCommandHandler.cs:38

    IStorageService storage)
    : ICommandHandler<ChangeFileVisibilityCommand, FileAssetDto>
{
    public async ValueTask<FileAssetDto> Handle(ChangeFileVisibilityCommand cmd, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(cmd);

        if (cmd.Visibility is not (Visibility.Public or Visibility.Private))
        {
            throw new CustomException(
                $"Unknown visibility value '{cmd.Visibility}'.",
                errors: null,
                System.Net.HttpStatusCode.BadRequest);
        }

        var f = await db.FileAssets
            .FirstOrDefaultAsync(x => x.Id == cmd.FileAssetId, cancellationToken)
            .ConfigureAwait(false)
            ?? throw new NotFoundException("file not found");

        var userId = currentUser.GetUserId().ToString();
        var policy = policies.Resolve(f.OwnerType)
            ?? throw new ForbiddenException("no policy");
        var ctx = new FileAccessContext(f.Id, f.OwnerType, f.OwnerId, f.CreatedByUserId, (int)f.Visibility);
        if (!await policy.CanChangeVisibilityAsync(ctx, userId, cancellationToken).ConfigureAwait(false))
        {
            throw new ForbiddenException("not allowed to change this file's visibility");
        }

        f.ChangeVisibility(cmd.Visibility);
        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);

        var publicUrl = f.Visibility == Visibility.Public
            ? storage.BuildPublicUrl(f.StorageKey)
            : null;
        return FileAssetMapper.ToDto(f, publicUrl);
    }

View on GitHub (pinned to 3f2959e683)