fullstackhero/dotnet-starter-kit · warning · NotFoundException

Product not found.

Error message

Product {command.ProductId} not found.

What it means

ChangeProductPrice loads the product by command.ProductId and throws NotFoundException when no product matches, so a price change cannot be applied to a missing aggregate. Soft-deleted products are filtered out and also report 'not found'.

Solutions

  1. Verify the product id exists in the target database.
  2. Refresh the pricing source (re-export the feed) so ids are current.
  3. Handle the 404 per row in bulk price updates and log skipped ids.
  4. Check the product was not soft-deleted; restore it if the update should proceed.
Defensive patterns

Strategy: try-catch

Validate before calling

const exists = await api.listProducts({ ids: [productId] });
if (!exists.items.length) return skipPriceUpdate(productId, 'product not found');

Type guard

const isGuid = (v) => typeof v === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v);

Try / catch

try { await api.changeProductPrice(productId, amount, currency); }
catch (e) { if (e.status === 404) log(`Product ${productId} missing; skipped`); else throw e; }

Prevention

When it happens

Trigger: POST price-change for a ProductId that: (1) was deleted, (2) is a typo/fabricated Guid, (3) belongs to another tenant.

Common situations: Pricing feed pushing updates for products that were removed since the feed was generated; wrong column mapped to productId in a CSV import; stale browser tab submitting an old id.

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

Appendix: source

Thrown at src/Modules/Catalog/Modules.Catalog/Features/v1/Products/ChangeProductPrice/ChangeProductPriceCommandHandler.cs:21

using FSH.Modules.Catalog.Contracts.v1.Products;
using FSH.Modules.Catalog.Data;
using FSH.Modules.Catalog.Domain;
using Mediator;
using Microsoft.EntityFrameworkCore;

namespace FSH.Modules.Catalog.Features.v1.Products.ChangeProductPrice;

public sealed class ChangeProductPriceCommandHandler(CatalogDbContext dbContext)
    : ICommandHandler<ChangeProductPriceCommand, Guid>
{
    public async ValueTask<Guid> Handle(ChangeProductPriceCommand command, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(command);

        var product = await dbContext.Products
            .FirstOrDefaultAsync(p => p.Id == command.ProductId, cancellationToken)
            .ConfigureAwait(false)
            ?? throw new NotFoundException($"Product {command.ProductId} not found.");

        product.ChangePrice(new Money(command.Amount, command.Currency));
        await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        return product.Id;
    }
}

View on GitHub (pinned to 3f2959e683)