fullstackhero/dotnet-starter-kit · error · NotFoundException

Brand not found.

Error message

Brand {command.BrandId} not found.

What it means

DeleteBrandCommandHandler looks up the brand by command.BrandId and throws NotFoundException when no matching row exists, so the delete is never executed. NotFoundException is mapped to HTTP 404 by the API pipeline. Soft-deleted brands are excluded by the default query filters, so a soft-deleted brand also yields this error.

Solutions

  1. Verify the BrandId exists via GET /brands/v1/{id} before deleting
  2. If the brand is soft-deleted, use the Restore endpoint instead of Delete
  3. Check the tenant header/context matches the tenant that owns the brand

Example fix

// before
await mediator.Send(new DeleteBrandCommand(unverifiedId));
// after
var brand = await mediator.Send(new GetBrandByIdQuery(id)); // 404 surfaces here first
await mediator.Send(new DeleteBrandCommand(brand.Id));
Defensive patterns

Strategy: try-catch

Validate before calling

try { await apiClient.GET($"/brands/v1/{id}"); return true; } catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound) { return false; }

Try / catch

try { await mediator.Send(new DeleteBrandCommand(id)); }
catch (NotFoundException) { /* treat as already deleted — safe to ignore */ }

Prevention

When it happens

Trigger: Send/DeleteBrand command with a BrandId that does not exist, was already hard-deleted, or belongs to another tenant.

Common situations: Stale ID cached in a client after the brand was deleted elsewhere; passing a product/category ID by mistake; cross-tenant ID due to a missing tenant header.

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/65a58e65b41cb05c. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Catalog/Modules.Catalog/Features/v1/Brands/DeleteBrand/DeleteBrandCommandHandler.cs:19

using FSH.Framework.Core.Exceptions;
using FSH.Modules.Catalog.Contracts.v1.Brands;
using FSH.Modules.Catalog.Data;
using Mediator;
using Microsoft.EntityFrameworkCore;

namespace FSH.Modules.Catalog.Features.v1.Brands.DeleteBrand;

public sealed class DeleteBrandCommandHandler(CatalogDbContext dbContext)
    : ICommandHandler<DeleteBrandCommand, Unit>
{
    public async ValueTask<Unit> Handle(DeleteBrandCommand command, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(command);

        var brand = await dbContext.Brands
            .FirstOrDefaultAsync(b => b.Id == command.BrandId, cancellationToken)
            .ConfigureAwait(false)
            ?? throw new NotFoundException($"Brand {command.BrandId} not found.");

        dbContext.Brands.Remove(brand);
        await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        return Unit.Value;
    }
}

View on GitHub (pinned to 3f2959e683)