fullstackhero/dotnet-starter-kit · error · NotFoundException
Brand not found.
Error message
Brand {command.BrandId} not found. What it means
RestoreBrandCommandHandler loads the brand with only the SoftDelete filter disabled, so only non-deleted rows (still tenant-scoped) are visible; it throws NotFoundException when nothing matches, mapped to HTTP 404. This means restoring an already-active brand, an unknown ID, or a foreign-tenant brand all produce this error — you cannot 'restore' something that is not currently soft-deleted.
Solutions
- Check whether the brand is already active via GET /brands/v1/{id} — if so, no restore is needed
- Verify the BrandId is correct and belongs to the current tenant
- If the row was hard-deleted, re-create the brand instead of restoring
Example fix
// before await mediator.Send(new RestoreBrandCommand(id)); // may already be active // after var existing = await mediator.Send(new GetBrandByIdQuery(id)); // 200 => nothing to restore if (existing is null) await mediator.Send(new RestoreBrandCommand(id));
Defensive patterns
Strategy: validation
Validate before calling
bool isActive = await apiClient.GET<bool>($"/brands/v1/{id}/exists");
if (isActive) return; // nothing to restore Try / catch
try { await mediator.Send(new RestoreBrandCommand(id)); }
catch (NotFoundException) { /* already active or never existed */ } Prevention
- Track soft-delete state client-side before calling restore
- Make restore calls idempotent (ignore not-found as success)
- Never restore across tenants
When it happens
Trigger: Send/RestoreBrand command with a BrandId that is not present as a soft-deleted row: already active, never existed, hard-deleted, or another tenant's brand.
Common situations: Double-invoking restore after the first call succeeded; guessing at IDs to recover data; pointing at a brand in the wrong tenant.
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/84fa2a01cd9633fa.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Catalog/Modules.Catalog/Features/v1/Brands/RestoreBrand/RestoreBrandCommandHandler.cs:23
using Mediator;
using Microsoft.EntityFrameworkCore;
namespace FSH.Modules.Catalog.Features.v1.Brands.RestoreBrand;
public sealed class RestoreBrandCommandHandler(CatalogDbContext dbContext)
: ICommandHandler<RestoreBrandCommand, Guid>
{
public async ValueTask<Guid> Handle(RestoreBrandCommand command, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(command);
// Disable only the SoftDelete filter so we can load a deleted row;
// tenant scoping stays in force, so cross-tenant restores cannot leak.
var brand = await dbContext.Brands
.IgnoreQueryFilters([QueryFilters.SoftDelete])
.FirstOrDefaultAsync(b => b.Id == command.BrandId, cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException($"Brand {command.BrandId} not found.");
brand.Restore();
await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
return brand.Id;
}
}
View on GitHub (pinned to 3f2959e683)