{"record":{"id":"aaeb719f495aee0e","repo":"fullstackhero/dotnet-starter-kit","slug":"brand-command-brandid-not-found-createproductcommandhandler","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/Products/CreateProduct/CreateProductCommandHandler.cs","lineNumber":24,"sourceCode":"using FSH.Modules.Catalog.Domain;\nusing Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Catalog.Features.v1.Products.CreateProduct;\n\npublic sealed class CreateProductCommandHandler(CatalogDbContext dbContext)\n    : ICommandHandler<CreateProductCommand, Guid>\n{\n    public async ValueTask<Guid> Handle(CreateProductCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        bool brandExists = await dbContext.Brands\n            .AnyAsync(b => b.Id == command.BrandId, cancellationToken)\n            .ConfigureAwait(false);\n        if (!brandExists)\n        {\n            throw new NotFoundException($\"Brand {command.BrandId} not found.\");\n        }\n\n        bool categoryExists = await dbContext.Categories\n            .AnyAsync(c => c.Id == command.CategoryId, cancellationToken)\n            .ConfigureAwait(false);\n        if (!categoryExists)\n        {\n            throw new NotFoundException($\"Category {command.CategoryId} not found.\");\n        }\n\n        var product = Product.Create(\n            command.Sku,\n            command.Name,\n            command.Description,\n            command.BrandId,\n            command.CategoryId,\n            new Money(command.PriceAmount, command.PriceCurrency),\n            command.Stock);","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Catalog/Modules.Catalog/Features/v1/Products/CreateProduct/CreateProductCommandHandler.cs#L6-L42","documentation":"CreateProductCommandHandler verifies the referenced Brand exists before creating a Product. If no Brand row matches command.BrandId, it throws NotFoundException, which the API layer maps to HTTP 404. This guards the FK relationship at the application layer before an insert is attempted.","triggerScenarios":"POST /products (v1) with a BrandId that does not exist in the Brands table — e.g. a deleted brand, a brand from another tenant filtered out by the tenant query filter, or a fabricated/mistyped GUID.","commonSituations":"Client caches brand IDs that were later deleted; multi-tenant apps where the brand belongs to a different tenant so the global query filter hides it; test fixtures seeding brands into a different database than the API uses.","solutions":["Query the Brands table (respecting tenant filters) to confirm the BrandId exists before calling the endpoint.","If the brand was deleted, create the product with an existing brand or recreate the brand first.","In multi-tenant setups, verify the request carries the correct tenant identifier so the brand row is visible.","If the brand should exist, check whether you are pointing at the right connection string/database/environment."],"exampleFix":"// before\nawait api.createProduct({ sku: 'X', name: 'X', brandId: cachedBrandId });\n// after\nconst brand = await api.getBrand(cachedBrandId); // throws/404s early if stale\nif (!brand) {\n  const fresh = await api.listBrands();\n  cachedBrandId = fresh.items[0].id;\n}\nawait api.createProduct({ sku: 'X', name: 'X', brandId: cachedBrandId });","handlingStrategy":"validation","validationCode":"// client-side pre-check\nconst brand = await api.getBrand(brandId).catch(() => null);\nif (!brand) throw new Error(`Brand ${brandId} does not exist`);","typeGuard":null,"tryCatchPattern":"try {\n  await api.createProduct(payload);\n} catch (e) {\n  if (e.status === 404 && /Brand .* not found/.test(e.message ?? '')) {\n    showFieldError('brandId', 'Selected brand no longer exists');\n    return;\n  }\n  throw e;\n}","preventionTips":["Populate brand pickers from a live API query, never from cached constants","Revalidate cached brand ids on form load","In multi-tenant apps, scope brand lists to the current tenant"],"tags":["ef-core","not-found","foreign-key","catalog","http-404"],"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"}