{"record":{"id":"706bee81ce17572f","repo":"dotnet/eShop","slug":"invalid-units","errorCode":null,"errorMessage":"Invalid units","messagePattern":"Invalid units","errorType":"exception","errorClass":"OrderingDomainException","httpStatus":null,"severity":"error","filePath":"src/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs","lineNumber":58,"sourceCode":"        Units = units;\n        PictureUrl = pictureUrl;\n    }\n    \n    public void SetNewDiscount(decimal discount)\n    {\n        if (discount < 0)\n        {\n            throw new OrderingDomainException(\"Discount is not valid\");\n        }\n\n        Discount = discount;\n    }\n\n    public void AddUnits(int units)\n    {\n        if (units < 0)\n        {\n            throw new OrderingDomainException(\"Invalid units\");\n        }\n\n        Units += units;\n    }\n}\n","sourceCodeStart":40,"sourceCodeEnd":64,"githubUrl":"https://github.com/dotnet/eShop/blob/9b4f9434f46fdc5c1a6e9e936af2868340cdbc48/src/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs#L40-L64","documentation":"Thrown by OrderItem.AddUnits when the supplied units argument is less than zero. AddUnits is meant to increment an existing line's quantity; a negative argument would silently decrement (or underflow) the line, so it is rejected as a domain invariant violation. It is an OrderingDomainException.","triggerScenarios":"Calling existingOrderForProduct.AddUnits(units) (via Order.AddOrderItem for an already-present product, or directly) with a negative delta. Arises from sign errors when merging duplicate lines, or from passing a delta computed as (newQty - oldQty) where newQty < oldQty.","commonSituations":"Client intent to update quantity downward routed through AddUnits instead of a dedicated remove path; merge logic subtracting and forwarding the negative remainder; a stale request replaying an old negative delta.","solutions":["Validate the delta is non-negative at the call site before AddUnits; route decreases through a separate decrement/remove-line path.","Fix the delta computation: if (newQty > oldQty) item.AddUnits(newQty - oldQty) else item.Remove/adjust via the appropriate method.","Add a handler-level guard: if (units < 0) reject the request.","Unit-test AddUnits with a negative argument to lock in the throw behavior."],"exampleFix":"// before\nitem.AddUnits(newQty - oldQty); // negative when decreasing\n\n// after\nvar delta = newQty - oldQty;\nif (delta > 0) item.AddUnits(delta);\nelse if (delta < 0) /* use the remove/adjust path */;","handlingStrategy":"validation","validationCode":"if (units < 0) throw new ArgumentOutOfRangeException(nameof(units));\nitem.AddUnits(units);","typeGuard":"static bool IsValidDelta(int units) => units >= 0;","tryCatchPattern":"try {\n    item.AddUnits(delta);\n} catch (OrderingDomainException ex) when (ex.Message == \"Invalid units\") {\n    // negative delta — route through the decrement/remove path instead\n}","preventionTips":["Use AddUnits only for increases; handle decreases via a dedicated method.","Guard delta >= 0 at the handler boundary.","Validate merge logic does not forward negative remainders."],"tags":["domain","ordering","validation","ddd"],"backgroundTag":null,"analyzedSha":"9b4f9434f46fdc5c1a6e9e936af2868340cdbc48","analyzedAt":"2026-08-13T19:29:36.594Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}