fullstackhero/dotnet-starter-kit · error · NotFoundException
Category not found.
Error message
Category {command.CategoryId} not found. What it means
CreateProductCommandHandler verifies the referenced Category exists before creating a Product. If no Category row matches command.CategoryId, it throws NotFoundException, mapped to HTTP 404. Like the brand check, it validates the FK relationship explicitly instead of relying on a DB constraint failure.
Solutions
- Verify the CategoryId exists via the categories list/get endpoint before creating the product.
- Recreate or reassign to a valid category if the original was deleted.
- Ensure the request targets the correct tenant so the category is not filtered out.
- Confirm the client is calling the intended environment (dev/staging/prod) with matching data.
Example fix
// before
await api.createProduct({ sku: 'X', name: 'X', categoryId: form.categoryId });
// after
const categories = await api.listCategories();
if (!categories.items.some(c => c.id === form.categoryId)) {
throw new Error('Pick a valid category before submitting');
}
await api.createProduct({ sku: 'X', name: 'X', categoryId: form.categoryId }); Defensive patterns
Strategy: validation
Validate before calling
const categories = await api.listCategories();
if (!categories.items.some(c => c.id === categoryId)) {
throw new Error(`Category ${categoryId} does not exist`);
} Try / catch
try {
await api.createProduct(payload);
} catch (e) {
if (e.status === 404 && /Category .* not found/.test(e.message ?? '')) {
showFieldError('categoryId', 'Selected category no longer exists');
return;
}
throw e;
} Prevention
- Drive category selection from the API's current category list
- Refresh category options when the tenant or environment changes
- Disable stale category options removed by other users
When it happens
Trigger: POST /products (v1) with a CategoryId that does not exist in the Categories table — deleted category, category in another tenant, or an invalid/mistyped identifier.
Common situations: Stale category pickers in clients after a category was removed; seed data differing between environments; tenant header mismatch hiding the category row.
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/8e67bda9593ac2dd.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Catalog/Modules.Catalog/Features/v1/Products/CreateProduct/CreateProductCommandHandler.cs:32
{
public async ValueTask<Guid> Handle(CreateProductCommand command, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(command);
bool brandExists = await dbContext.Brands
.AnyAsync(b => b.Id == command.BrandId, cancellationToken)
.ConfigureAwait(false);
if (!brandExists)
{
throw new NotFoundException($"Brand {command.BrandId} not found.");
}
bool categoryExists = await dbContext.Categories
.AnyAsync(c => c.Id == command.CategoryId, cancellationToken)
.ConfigureAwait(false);
if (!categoryExists)
{
throw new NotFoundException($"Category {command.CategoryId} not found.");
}
var product = Product.Create(
command.Sku,
command.Name,
command.Description,
command.BrandId,
command.CategoryId,
new Money(command.PriceAmount, command.PriceCurrency),
command.Stock);
bool skuTaken = await dbContext.Products
.AnyAsync(p => p.Sku == product.Sku, cancellationToken)
.ConfigureAwait(false);
if (skuTaken)
{
throw new CustomException(
$"A product with SKU '{product.Sku}' already exists.",View on GitHub (pinned to 3f2959e683)