kgrzybek/modular-monolith-with-ddd · error · InvalidCommandException
Pricelist item for deactivation must exist.
Error message
Pricelist item for deactivation must exist.
What it means
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).
Source
Thrown at src/Modules/Payments/Application/PriceListItems/DeactivatePriceListItem/DeactivatePriceListItemCommandHandler.cs:23
namespace CompanyName.MyMeetings.Modules.Payments.Application.PriceListItems.DeactivatePriceListItem
{
internal class DeactivatePriceListItemCommandHandler : ICommandHandler<DeactivatePriceListItemCommand>
{
private readonly IAggregateStore _aggregateStore;
public DeactivatePriceListItemCommandHandler(IAggregateStore aggregateStore)
{
_aggregateStore = aggregateStore;
}
public async Task Handle(DeactivatePriceListItemCommand command, CancellationToken cancellationToken)
{
var priceListItem = await _aggregateStore.Load(new PriceListItemId(command.PriceListItemId));
if (priceListItem == null)
{
throw new InvalidCommandException(["Pricelist item for deactivation must exist."]);
}
priceListItem.Deactivate();
_aggregateStore.AppendChanges(priceListItem);
}
}
}View on GitHub (pinned to 91c8ef24b4)
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.
Example fix
// before await _commandDispatcher.SendAsync(new DeactivatePriceListItemCommand(itemId)); // after var item = await _priceListQueries.GetItemAsync(itemId); if (item is null) return NoContent(); // already deactivated/absent await _commandDispatcher.SendAsync(new DeactivatePriceListItemCommand(itemId));
Defensive patterns
Strategy: validation
Validate before calling
var item = await _priceListQueries.GetItemAsync(itemId); if (item is null) return NoContent(); // already deactivated await _commandDispatcher.SendAsync(new DeactivatePriceListItemCommand(itemId));
Type guard
public static bool IsValidItemId(DeactivatePriceListItemCommand c) =>
c.PriceListItemId != Guid.Empty; Try / catch
try { await _commandDispatcher.SendAsync(cmd); }
catch (InvalidCommandException ex) when (ex.Errors.Any(m => m.Contains("must exist")))
{ return NoContent(); } // idempotent deactivate Prevention
- 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.
When it happens
Trigger: Dispatching DeactivatePriceListItemCommand for an id with no aggregate: already deleted, wrong id, wrong tenant, or missing event stream.
Common situations: Bulk deactivate job references stale ids; admin deactivates from a cached list; idempotent deactivate of an already-removed item.
Related errors
- Pricelist item for activation must exist.
- Pricelist item for changing must exist.
- Subscription for renewal must exist.
- Meeting commenting configuration for disabling commenting mu
- Meeting commenting configuration for enabling commenting mus
AI-assisted analysis of kgrzybek/modular-monolith-with-ddd@91c8ef24b4 (2026-08-13).
Data as JSON: /api/errors/0fd092a2fd6ee50d.
Report an issue: GitHub.