{"record":{"id":"65a58e65b41cb05c","repo":"fullstackhero/dotnet-starter-kit","slug":"brand-command-brandid-not-found","errorCode":null,"errorMessage":"Brand {command.BrandId} not found.","messagePattern":"Brand (.+?) not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Catalog/Modules.Catalog/Features/v1/Brands/DeleteBrand/DeleteBrandCommandHandler.cs","lineNumber":19,"sourceCode":"using FSH.Framework.Core.Exceptions;\nusing FSH.Modules.Catalog.Contracts.v1.Brands;\nusing FSH.Modules.Catalog.Data;\nusing Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Catalog.Features.v1.Brands.DeleteBrand;\n\npublic sealed class DeleteBrandCommandHandler(CatalogDbContext dbContext)\n    : ICommandHandler<DeleteBrandCommand, Unit>\n{\n    public async ValueTask<Unit> Handle(DeleteBrandCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        var brand = await dbContext.Brands\n            .FirstOrDefaultAsync(b => b.Id == command.BrandId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException($\"Brand {command.BrandId} not found.\");\n\n        dbContext.Brands.Remove(brand);\n        await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n        return Unit.Value;\n    }\n}\n","sourceCodeStart":1,"sourceCodeEnd":26,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Catalog/Modules.Catalog/Features/v1/Brands/DeleteBrand/DeleteBrandCommandHandler.cs#L1-L26","documentation":"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.","triggerScenarios":"Send/DeleteBrand command with a BrandId that does not exist, was already hard-deleted, or belongs to another tenant.","commonSituations":"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.","solutions":["Verify the BrandId exists via GET /brands/v1/{id} before deleting","If the brand is soft-deleted, use the Restore endpoint instead of Delete","Check the tenant header/context matches the tenant that owns the brand"],"exampleFix":"// before\nawait mediator.Send(new DeleteBrandCommand(unverifiedId));\n// after\nvar brand = await mediator.Send(new GetBrandByIdQuery(id)); // 404 surfaces here first\nawait mediator.Send(new DeleteBrandCommand(brand.Id));","handlingStrategy":"try-catch","validationCode":"try { await apiClient.GET($\"/brands/v1/{id}\"); return true; } catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound) { return false; }","typeGuard":null,"tryCatchPattern":"try { await mediator.Send(new DeleteBrandCommand(id)); }\ncatch (NotFoundException) { /* treat as already deleted — safe to ignore */ }","preventionTips":["Refresh entity lists before delete operations in UIs","Cache brand IDs with invalidation on delete events","Always send the correct tenant header"],"tags":["not-found","http-404","catalog","delete"],"backgroundTag":"entity-not-found","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}