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
- Verify the FileAssetId exists and is not soft-deleted: query FileAssets ignoring filters, e.g. db.FileAssets.IgnoreQueryFilters().AnyAsync(x => x.Id == id).
- Check the request is sent under the correct tenant (tenant header/route) so the global query filter does not hide the row.
- Refresh the client-side file list; remove stale cached ids after delete operations.
- 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
- Always take FileAssetId from the server's create/init response, never fabricate it.
- Invalidate client caches after delete operations.
- Remember tenant + soft-delete filters make valid-looking ids 'not found'.
- Keep ids as GUIDs end to end to avoid formatting loss.
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)