{"record":{"id":"1170b362c2e06bca","repo":"fullstackhero/dotnet-starter-kit","slug":"product-command-productid-not-found-1170b3","errorCode":null,"errorMessage":"Product {command.ProductId} not found.","messagePattern":"Product (.+?) not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Catalog/Modules.Catalog/Features/v1/Products/ReorderProductImages/ReorderProductImagesCommandHandler.cs","lineNumber":19,"sourceCode":"using FSH.Framework.Core.Exceptions;\nusing FSH.Modules.Catalog.Contracts.v1.Products.ReorderProductImages;\nusing FSH.Modules.Catalog.Data;\nusing Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Catalog.Features.v1.Products.ReorderProductImages;\n\npublic sealed class ReorderProductImagesCommandHandler(CatalogDbContext dbContext)\n    : ICommandHandler<ReorderProductImagesCommand, Unit>\n{\n    public async ValueTask<Unit> Handle(ReorderProductImagesCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        var product = await dbContext.Products\n            .FirstOrDefaultAsync(p => p.Id == command.ProductId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException($\"Product {command.ProductId} not found.\");\n\n        product.ReorderImages(command.OrderedImageIds);\n        await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n        return Unit.Value;\n    }\n}\n","sourceCodeStart":1,"sourceCodeEnd":26,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Catalog/Modules.Catalog/Features/v1/Products/ReorderProductImages/ReorderProductImagesCommandHandler.cs#L1-L26","documentation":"ReorderProductImagesCommandHandler loads the product for command.ProductId and throws NotFoundException when no matching product exists (HTTP 404), before delegating to product.ReorderImages. The product must exist and be visible under current query filters (tenant, not soft-deleted).","triggerScenarios":"Reorder call with a nonexistent/soft-deleted/other-tenant ProductId, or a drag-and-drop submit fired after the product was deleted in another tab/session.","commonSituations":"Stale editor page submitting a reorder after the product was deleted elsewhere; ids from a different tenant/environment; retrying a reorder whose product was removed meanwhile.","solutions":["Re-fetch the product before submitting reorder state; if 404, redirect back to the list.","Catch the 404 and surface 'this product no longer exists' instead of a raw error.","Ensure the OrderedImageIds array belongs to the same product as ProductId.","Verify tenant context and that the product is not soft-deleted."],"exampleFix":"// before\nawait api.reorderProductImages(productId, orderedIds);\n// after\ntry {\n  await api.reorderProductImages(productId, orderedIds);\n} catch (e) {\n  if (e.status === 404) {\n    notify('Product no longer exists');\n    navigate('/products');\n    return;\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"const product = await api.getProduct(productId).catch(() => null);\nif (!product) { navigate('/products'); return; }","typeGuard":null,"tryCatchPattern":"try {\n  await api.reorderProductImages(productId, orderedIds);\n} catch (e) {\n  if (e.status === 404) {\n    notify('Product no longer exists');\n    navigate('/products');\n    return;\n  }\n  throw e;\n}","preventionTips":["Block reorder submission when the editor's product data is stale","Close/redirect editors whose product was deleted elsewhere","Ensure ordered image ids originate from the same product fetch"],"tags":["ef-core","not-found","images","http-404","catalog"],"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"}