fix: custom decorators ignore `stopAtFirstError` option
Author: EizFerenCreated Sep 7, 2023Updated Jul 30, 2026
Labelstype: fixstatus: needs triage
Description
I've got a strange bug, when my custom decorators are stopping each other even with stopAtFirstError option set to false. Perharps standard class validator default decorators working as expected.
Minimal code-snippet showcasing the problem
class SomeDto {
@IsStrongPassword({
minNumbers: 1,
minLowercase: 0,
minUppercase: 0,
minSymbols: 0,
}, {
message: 'VALIDATION_PASSWORD_STRENTGH',
})
@MaxLength(20, {
message: 'VALIDATION_PASSWORD_LENGTH_MAX',
})
@MinLength(8, {
message: 'VALIDATION_PASSWORD_LENGTH_MIN',
})
@Requires('currentPassword', { // custom decorator, checks `currentPassword` field is defined
message: 'VALIDATION_CURRENT_PASSWORD_MISSING',
})
@Requires('newPasswordRepeat', { // custom decorator, checks `newPasswordRepeat` field is defined
message: 'VALIDATION_PASSWORD_REPEAT_MISSING',
})
@IsOptional()
newPassword?: string;
}Input, provided for this dto:
{
"newPassword": "qwe"
}Custom decorator definition:
import {
registerDecorator,
ValidationArguments,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
export function Requires(property: string, validationOptions?: ValidationOptions) {
return (object: any, propertyName: string) => {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [
property,
],
validator: RequiresConstraint,
});
};
}
@ValidatorConstraint({
name: 'Requires',
})
class RequiresConstraint implements ValidatorConstraintInterface {
validate(value: any, args: ValidationArguments) {
const [ relatedPropertyName ] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return relatedValue !== undefined;
}
}
Expected behavior
I'm expecting all custom decorator checks to be done, as below:
{
"messages": [
"VALIDATION_CURRENT_PASSWORD_MISSING",
"VALIDATION_PASSWORD_REPEAT_MISSING",
"VALIDATION_PASSWORD_LENGTH_MIN",
"VALIDATION_PASSWORD_STRENGTH"
]
}Actual behavior
Check for newPasswordRepeat field was skipped
{
"messages": [
"VALIDATION_CURRENT_PASSWORD_MISSING",
"VALIDATION_PASSWORD_LENGTH_MIN",
"VALIDATION_PASSWORD_STRENGTH"
]
}Source: typestack/class-validator