{"record":{"id":"647a69414dea57ba","repo":"kgrzybek/modular-monolith-with-ddd","slug":"command-validation-error","errorCode":null,"errorMessage":"Command validation error","messagePattern":"Command validation error","errorType":"validation","errorClass":"InvalidCommandException","httpStatus":400,"severity":"warning","filePath":"src/Modules/UserAccess/Infrastructure/Configuration/Processing/ValidationCommandHandlerDecorator.cs","lineNumber":32,"sourceCode":"        public ValidationCommandHandlerDecorator(\n            IList<IValidator<T>> validators,\n            ICommandHandler<T> decorated)\n        {\n            this._validators = validators;\n            _decorated = decorated;\n        }\n\n        public async Task Handle(T command, CancellationToken cancellationToken)\n        {\n            var errors = _validators\n                .Select(v => v.Validate(command))\n                .SelectMany(result => result.Errors)\n                .Where(error => error != null)\n                .ToList();\n\n            if (errors.Any())\n            {\n                throw new InvalidCommandException(errors.Select(x => x.ErrorMessage).ToList());\n            }\n\n            await _decorated.Handle(command, cancellationToken);\n        }\n    }\n}","sourceCodeStart":14,"sourceCodeEnd":38,"githubUrl":"https://github.com/kgrzybek/modular-monolith-with-ddd/blob/91c8ef24b4cb6ef558c95d8267fa07d68c7059f8/src/Modules/UserAccess/Infrastructure/Configuration/Processing/ValidationCommandHandlerDecorator.cs#L14-L38","documentation":"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.","triggerScenarios":"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.","commonSituations":"Login with blank credentials; password change that violates policy; admin form missing role assignment; client skips client-side validation.","solutions":["Inspect ex.Errors for the specific failing validators (the top-level message is generic).","Correct the command values to satisfy each validator.","Mirror validators on the client to fail fast.","Update validator rules if a legitimate value is rejected."],"exampleFix":"// before\nawait _commandDispatcher.SendAsync(new ChangeUserPasswordCommand(login, \"\", \"\"));\n\n// after\nawait _commandDispatcher.SendAsync(new ChangeUserPasswordCommand(login, \"OldP@ss1!\", \"NewStrongP@ss1!\"));","handlingStrategy":"validation","validationCode":"var validator = new ChangeUserPasswordCommandValidator();\nvar result = await validator.ValidateAsync(cmd);\nif (!result.IsValid) return BadRequest(result.Errors.Select(e => e.ErrorMessage));\nawait _commandDispatcher.SendAsync(cmd);","typeGuard":null,"tryCatchPattern":"try { await _commandDispatcher.SendAsync(cmd); }\ncatch (InvalidCommandException ex)\n{ return BadRequest(new { errors = ex.Errors }); } // note: top-level message is generic; detail is in Errors","preventionTips":["Enforce password policy on the client before submit.","Do not rely on the generic message; always surface ex.Errors.","Keep validators in sync with UserAccess command DTOs."],"tags":["user-access","validation","fluentvalidation","command-handler","invalid-command-exception","decorator"],"backgroundTag":null,"analyzedSha":"91c8ef24b4cb6ef558c95d8267fa07d68c7059f8","analyzedAt":"2026-08-13T17:18:16.571Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}