{"record":{"id":"0fd092a2fd6ee50d","repo":"kgrzybek/modular-monolith-with-ddd","slug":"pricelist-item-for-deactivation-must-exist","errorCode":null,"errorMessage":"Pricelist item for deactivation must exist.","messagePattern":"Pricelist item for deactivation must exist\\.","errorType":"validation","errorClass":"InvalidCommandException","httpStatus":400,"severity":"error","filePath":"src/Modules/Payments/Application/PriceListItems/DeactivatePriceListItem/DeactivatePriceListItemCommandHandler.cs","lineNumber":23,"sourceCode":"\r\nnamespace CompanyName.MyMeetings.Modules.Payments.Application.PriceListItems.DeactivatePriceListItem\r\n{\r\n    internal class DeactivatePriceListItemCommandHandler : ICommandHandler<DeactivatePriceListItemCommand>\r\n    {\r\n        private readonly IAggregateStore _aggregateStore;\r\n\r\n        public DeactivatePriceListItemCommandHandler(IAggregateStore aggregateStore)\r\n        {\r\n            _aggregateStore = aggregateStore;\r\n        }\r\n\r\n        public async Task Handle(DeactivatePriceListItemCommand command, CancellationToken cancellationToken)\r\n        {\r\n            var priceListItem = await _aggregateStore.Load(new PriceListItemId(command.PriceListItemId));\r\n\r\n            if (priceListItem == null)\r\n            {\r\n                throw new InvalidCommandException([\"Pricelist item for deactivation must exist.\"]);\r\n            }\r\n\r\n            priceListItem.Deactivate();\r\n\r\n            _aggregateStore.AppendChanges(priceListItem);\r\n        }\r\n    }\r\n}","sourceCodeStart":5,"sourceCodeEnd":31,"githubUrl":"https://github.com/kgrzybek/modular-monolith-with-ddd/blob/91c8ef24b4cb6ef558c95d8267fa07d68c7059f8/src/Modules/Payments/Application/PriceListItems/DeactivatePriceListItem/DeactivatePriceListItemCommandHandler.cs#L5-L31","documentation":"Thrown by DeactivatePriceListItemCommandHandler when _aggregateStore.Load returns null for the PriceListItemId. Deactivation requires the aggregate to exist; a missing item aborts with InvalidCommandException (HTTP 400).","triggerScenarios":"Dispatching DeactivatePriceListItemCommand for an id with no aggregate: already deleted, wrong id, wrong tenant, or missing event stream.","commonSituations":"Bulk deactivate job references stale ids; admin deactivates from a cached list; idempotent deactivate of an already-removed item.","solutions":["Verify the item exists before deactivation; treat 'already gone' as success if idempotent.","Pre-check existence in the controller and return 404/204.","Refresh the admin list to drop stale ids.","Map InvalidCommandException to 400/404 at the API boundary."],"exampleFix":"// before\nawait _commandDispatcher.SendAsync(new DeactivatePriceListItemCommand(itemId));\n\n// after\nvar item = await _priceListQueries.GetItemAsync(itemId);\nif (item is null) return NoContent(); // already deactivated/absent\nawait _commandDispatcher.SendAsync(new DeactivatePriceListItemCommand(itemId));","handlingStrategy":"validation","validationCode":"var item = await _priceListQueries.GetItemAsync(itemId);\nif (item is null) return NoContent(); // already deactivated\nawait _commandDispatcher.SendAsync(new DeactivatePriceListItemCommand(itemId));","typeGuard":"public static bool IsValidItemId(DeactivatePriceListItemCommand c) =>\n    c.PriceListItemId != Guid.Empty;","tryCatchPattern":"try { await _commandDispatcher.SendAsync(cmd); }\ncatch (InvalidCommandException ex) when (ex.Errors.Any(m => m.Contains(\"must exist\")))\n{ return NoContent(); } // idempotent deactivate","preventionTips":["Treat a missing item on deactivate as success when idempotent.","Refresh bulk-deactivate job inputs to avoid stale ids.","Reject empty Guid at the controller."],"tags":["payments","price-list","not-found","invalid-command-exception","command-handler"],"backgroundTag":null,"analyzedSha":"91c8ef24b4cb6ef558c95d8267fa07d68c7059f8","analyzedAt":"2026-08-13T17:18:16.571Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}