nestjs/nest · error · BadRequestException
Validation failed
Error message
Validation failed
What it means
A custom ValidationPipe (sample/36-hmr-esm/src/common/pipes/validation.pipe.ts) that runs class-transformer's plainToInstance then class-validator's validate against the handler's metatype (the DTO). At line 21 it throws BadRequestException('Validation failed') when any validation errors are produced. It is a stripped-down reimplementation of NestJS's global ValidationPipe, used by the HMR/ESM sample. The toValidate() guard skips JS primitives (String, Boolean, Number, Array, Object) so only DTO classes get validated.
Source
Thrown at sample/36-hmr-esm/src/common/pipes/validation.pipe.ts:21
BadRequestException,
Injectable,
PipeTransform,
Type,
} from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
@Injectable()
export class ValidationPipe implements PipeTransform<any> {
async transform(value: any, metadata: ArgumentMetadata) {
const { metatype } = metadata;
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToInstance(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('Validation failed');
}
return value;
}
private toValidate(metatype: Type<any>): boolean {
const types = [String, Boolean, Number, Array, Object];
return !types.find(type => metatype === type);
}
}
View on GitHub (pinned to 6ec0e2783d)
Solutions
- Add the missing class-validator decorators to the DTO (e.g., @IsString(), @IsInt(), @IsNotEmpty()).
- Send a payload matching the DTO contract from the client.
- Consider the built-in ValidationPipe with whitelist, forbidNonWhitelished, and transform enabled for stronger guarantees than the sample.
- Expand the thrown error to include the validation errors array for actionable client feedback.
Example fix
// before
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('Validation failed');
}
// after
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException(
errors.map(e => Object.values(e.constraints ?? {}).join(', ')).join('; '),
);
} Defensive patterns
Strategy: validation
Validate before calling
// Client-side guard before posting
function looksLikeDto(v: unknown): v is MyDto {
return typeof v === 'object' && v !== null
&& typeof (v as any).name === 'string'
&& Number.isFinite((v as any).quantity);
}
if (!looksLikeDto(body)) throw new Error('payload does not match DTO'); Type guard
import { validateOrReject } from 'class-validator';
import { plainToInstance } from 'class-transformer';
async function isValidDto<T>(cls: new () => T, raw: unknown): Promise<boolean> {
try {
await validateOrReject(plainToInstance(cls, raw));
return true;
} catch {
return false;
}
} Try / catch
try {
return await controller.handler(payload); // pipe runs
} catch (e) {
if (e instanceof BadRequestException) {
return reply.code(400).send({ message: e.message, details: e.getResponse() });
}
throw e;
} Prevention
- Put validation decorators (@IsString, @IsInt, etc.) on every DTO field.
- Prefer the built-in global ValidationPipe with whitelist + forbidNonWhitelisted + transform.
- Return the class-validator errors array in the message for actionable feedback.
- Write a unit test per DTO that asserts invalid payloads are rejected.
When it happens
Trigger: POST/PUT/MUTATION whose body violates the DTO's class-validator decorators: missing required @IsString/@IsInt fields, wrong primitive types, out-of-range numbers (@Min/@Max), or failing custom constraints.
Common situations: DTO class has NO decorators → validate returns [] and bad payloads slip through (the pipe silently passes); nested objects aren't validated because plainToInstance isn't recursive without transform options; the pipe isn't registered globally so some routes skip it; whitelist semantics differ from the built-in pipe.
Related errors
- Validation failed
- 3
- Unauthorized
- The "connect()" method is not supported in gRPC mode.
- Method is not supported in gRPC mode. Use ClientGrpc instead
AI-assisted analysis of nestjs/nest@6ec0e2783d (2026-08-03).
Data as JSON: /data/errors/0fdc74c4e522196a.json.
Report an issue: GitHub.