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

Command validation error

Error message

Command validation error

What it means

Thrown by the UserAccess module ValidationCommandHandlerDecorator. It wraps every UserAccess command handler, runs all FluentValidation validators for command T, and throws InvalidCommandException(errors) — whose message is the literal 'Command validation error' while the real detail lives in the Errors list. Distinguishes itself from Payments/Registrations by using a fixed summary message rather than forwarding validator messages as the exception message.

Source

Thrown at src/Modules/UserAccess/Infrastructure/Configuration/Processing/ValidationCommandHandlerDecorator.cs:32

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

        public async Task 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());
            }

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

View on GitHub (pinned to 91c8ef24b4)

Solutions

  1. Inspect ex.Errors for the specific failing validators (the top-level message is generic).
  2. Correct the command values to satisfy each validator.
  3. Mirror validators on the client to fail fast.
  4. Update validator rules if a legitimate value is rejected.

Example fix

// before
await _commandDispatcher.SendAsync(new ChangeUserPasswordCommand(login, "", ""));

// after
await _commandDispatcher.SendAsync(new ChangeUserPasswordCommand(login, "OldP@ss1!", "NewStrongP@ss1!"));
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

try { await _commandDispatcher.SendAsync(cmd); }
catch (InvalidCommandException ex)
{ return BadRequest(new { errors = ex.Errors }); } // note: top-level message is generic; detail is in Errors

Prevention

When it happens

Trigger: Sending a UserAccess command (login, change password, add user, assign role) with properties failing a registered validator: empty login, password not meeting policy, missing role name, malformed email.

Common situations: Login with blank credentials; password change that violates policy; admin form missing role assignment; client skips client-side validation.

Related errors


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