{"record":{"id":"3a0300c54001420d","repo":"fullstackhero/dotnet-starter-kit","slug":"a-product-with-sku-product-sku-already-exists","errorCode":null,"errorMessage":"A product with SKU '{product.Sku}' already exists.","messagePattern":"A product with SKU '(.+?)' already exists\\.","errorType":"exception","errorClass":"CustomException","httpStatus":409,"severity":"error","filePath":"src/Modules/Catalog/Modules.Catalog/Features/v1/Products/CreateProduct/CreateProductCommandHandler.cs","lineNumber":49,"sourceCode":"        {\n            throw new NotFoundException($\"Category {command.CategoryId} not found.\");\n        }\n\n        var product = Product.Create(\n            command.Sku,\n            command.Name,\n            command.Description,\n            command.BrandId,\n            command.CategoryId,\n            new Money(command.PriceAmount, command.PriceCurrency),\n            command.Stock);\n\n        bool skuTaken = await dbContext.Products\n            .AnyAsync(p => p.Sku == product.Sku, cancellationToken)\n            .ConfigureAwait(false);\n        if (skuTaken)\n        {\n            throw new CustomException(\n                $\"A product with SKU '{product.Sku}' already exists.\",\n                (IEnumerable<string>?)null,\n                HttpStatusCode.Conflict);\n        }\n\n        bool slugTaken = await dbContext.Products\n            .AnyAsync(p => p.Slug == product.Slug, cancellationToken)\n            .ConfigureAwait(false);\n        if (slugTaken)\n        {\n            throw new CustomException(\n                $\"A product with name '{command.Name}' already exists.\",\n                (IEnumerable<string>?)null,\n                HttpStatusCode.Conflict);\n        }\n\n        dbContext.Products.Add(product);\n        await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Catalog/Modules.Catalog/Features/v1/Products/CreateProduct/CreateProductCommandHandler.cs#L31-L67","documentation":"CreateProductCommandHandler checks whether any Product already uses the same SKU before inserting. If taken, it throws CustomException with HttpStatusCode.Conflict, producing HTTP 409. SKU is a unique business key, so duplicates are rejected explicitly rather than by a DB unique constraint.","triggerScenarios":"POST /products (v1) whose derived Product.Sku equals the Sku of any existing product row — duplicate form submission, retry of a request that actually succeeded, or importing a catalog that already contains the SKU.","commonSituations":"Double-clicking 'Create' or replaying a POST after a timeout; CSV/bulk import colliding with existing inventory; two environments sharing one database.","solutions":["Check SKU availability first (list/search products by SKU) and use a different SKU.","If this is a retry, GET the product by SKU — the original create likely succeeded; reuse the existing product instead of re-posting.","Add client-side idempotency: disable the submit button while the request is in flight.","If a unique constraint violation surfaces instead, it means the app-level check raced — catch 409/unique-violation and surface a friendly message."],"exampleFix":"// before\nawait api.createProduct({ sku: form.sku, name: form.name });\n// after\nconst existing = await api.searchProducts({ sku: form.sku });\nif (existing.items.length > 0) {\n  throw new Error(`SKU ${form.sku} is already in use`);\n}\nawait api.createProduct({ sku: form.sku, name: form.name });","handlingStrategy":"validation","validationCode":"const dup = await api.searchProducts({ sku });\nif (dup.items.length > 0) throw new Error(`SKU ${sku} already in use`);","typeGuard":null,"tryCatchPattern":"try {\n  await api.createProduct(payload);\n} catch (e) {\n  if (e.status === 409) {\n    showError(`SKU ${payload.sku} already exists`);\n    return;\n  }\n  throw e;\n}","preventionTips":["Debounce submit buttons and disable while the create request is in flight","Pre-validate SKU uniqueness with a lookup before POST","Use idempotency keys or check-then-fetch on retries of failed creates"],"tags":["conflict","duplicate","sku","catalog","http-409"],"backgroundTag":"duplicate-resource-conflict","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"}