kgrzybek/modular-monolith-with-ddd · warning · InvalidCommandException

{validator error messages}

Error message

{validator error messages}

What it means

Thrown by the Payments module ValidationCommandHandlerWithResultDecorator, the TResult-returning counterpart of the validation decorator. It runs all FluentValidation validators for command T, aggregates the ErrorMessages, and throws InvalidCommandException(errors) when any validation fails. The placeholder '{validator error messages}' is replaced by the actual validator messages at throw time.

Source

Thrown at src/Modules/Payments/Infrastructure/Configuration/Processing/ValidationCommandHandlerWithResultDecorator.cs:32

        public ValidationCommandHandlerWithResultDecorator(
            IList<IValidator<T>> validators,
            ICommandHandler<T, TResult> decorated)
        {
            this._validators = validators;
            _decorated = decorated;
        }

        public Task<TResult> Handle(T command, CancellationToken cancellationToken)
        {
            var errors = _validators
                .Select(v => v.Validate(command))
                .SelectMany(result => result.Errors)
                .Where(error => error != null)
                .ToList();

            if (errors.Any())
            {
                throw new InvalidCommandException(errors.Select(x => x.ErrorMessage).ToList());
            }

            return _decorated.Handle(command, cancellationToken);
        }
    }
}

View on GitHub (pinned to 91c8ef24b4)

Solutions

  1. Read ex.Errors to identify the failing validators and properties.
  2. Correct the command values to satisfy validation.
  3. Pre-validate on the client/API layer using the same validators.
  4. Adjust validator rules if a valid business value is being rejected.

Example fix

// before
var id = await _commandDispatcher.SendAsync(new BuySubscriptionRenewalCommand(Guid.Empty, "", "", 0, ""));

// after
var id = await _commandDispatcher.SendAsync(new BuySubscriptionRenewalCommand(subscriptionId, "MONTHLY", "PL", 49.99m, "PLN"));
Defensive patterns

Strategy: validation

Validate before calling

var validator = new BuySubscriptionRenewalCommandValidator();
var result = await validator.ValidateAsync(cmd);
if (!result.IsValid) return BadRequest(result.Errors.Select(e => e.ErrorMessage));
var id = await _commandDispatcher.SendAsync(cmd);

Try / catch

try { var id = await _commandDispatcher.SendAsync(cmd); }
catch (InvalidCommandException ex)
{ return BadRequest(new { errors = ex.Errors }); }

Prevention

When it happens

Trigger: Sending any Payments command that returns a result (e.g. BuySubscriptionRenewalCommand returns Guid) with properties that fail a registered validator.

Common situations: Same as the void decorator but on result-returning commands: missing required fields, invalid enum codes, malformed ids, bad money values.

Related errors


AI-assisted analysis of kgrzybek/modular-monolith-with-ddd@91c8ef24b4 (2026-08-13). Data as JSON: /api/errors/5b8067ef9d760912. Report an issue: GitHub.