fullstackhero/dotnet-starter-kit · error · NotFoundException

Image not found on product .

Error message

Image {command.ImageId} not found on product {command.ProductId}.

What it means

RemoveProductImageCommandHandler verifies the requested ImageId exists in the product's Images collection before calling the domain; if not, it throws NotFoundException (HTTP 404). The comment in source notes the domain would otherwise throw an untranslated InvalidOperationException, so this check maps the miss to a proper 404.

Solutions

  1. Re-fetch the product's images and confirm the image id is still present before calling remove.
  2. Treat 404 'image not found' as idempotent success in the UI — the goal state (image gone) already holds.
  3. Refresh the image gallery after every removal so stale ids aren't reused.
  4. Verify the image id belongs to the same product you are targeting.

Example fix

// before
await api.removeProductImage(productId, imageId); // may 404
// after
try {
  await api.removeProductImage(productId, imageId);
} catch (e) {
  if (e.status === 404) return; // already removed — idempotent
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const product = await api.getProduct(productId);
if (!product.images.some(i => i.id === imageId)) return; // already gone

Try / catch

try {
  await api.removeProductImage(productId, imageId);
} catch (e) {
  if (e.status === 404) return; // idempotent: image already removed
  throw e;
}

Prevention

When it happens

Trigger: Removing an image id that is not part of the given product's image collection — already-removed image, image id belonging to a different product, or a client-side stale image list.

Common situations: Two operators removing the same image concurrently (second gets 404); UI cache of images not refreshed after a prior removal; copying image ids between products.

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

Appendix: source

Thrown at src/Modules/Catalog/Modules.Catalog/Features/v1/Products/RemoveProductImage/RemoveProductImageCommandHandler.cs:24

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

public sealed class RemoveProductImageCommandHandler(CatalogDbContext dbContext)
    : ICommandHandler<RemoveProductImageCommand, Unit>
{
    public async ValueTask<Unit> Handle(RemoveProductImageCommand 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.");

        // Domain throws InvalidOperationException for unknown imageId; translate to 404.
        if (!product.Images.Any(i => i.Id == command.ImageId))
        {
            throw new NotFoundException($"Image {command.ImageId} not found on product {command.ProductId}.");
        }

        product.RemoveImage(command.ImageId);
        await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        return Unit.Value;
    }
}

View on GitHub (pinned to 3f2959e683)