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

  1. Verify the CategoryId exists via the categories list/get endpoint before creating the product.
  2. Recreate or reassign to a valid category if the original was deleted.
  3. Ensure the request targets the correct tenant so the category is not filtered out.
  4. 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

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)