Skip to content

Conversation

@at7211
Copy link
Contributor

@at7211 at7211 commented Feb 9, 2026

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

When using ValidationPipe with nested object validation, custom error messages are prepended with the parent property path. For example, a custom message like
"name should not be empty" becomes "parent.name should not be empty", which modifies the user-defined message unexpectedly.

Issue Number: #16268

What is the new behavior?

Add a new errorFormat option to ValidationPipeOptions with two modes:

  • 'list' (default): Current behavior - returns an array of error message strings with parent path prepended to messages. No breaking change.
  • 'grouped': Returns a Record<string, string[]> where keys are dot-notation property paths and values are arrays of unmodified constraint messages.

Example with errorFormat: 'grouped':

app.useGlobalPipes(new ValidationPipe({                                                                                                                              
  errorFormat: 'grouped',                                                                                                                                            
}));                                                                                                                                                                 
                                                                                                                                                                     
Response:                                                                                                                                                            
{                                                                                                                                                                    
  "message": {                                                                                                                                                       
    "parent.child": ["name should not be empty", "child must be a string"],                                                                                          
    "text": ["text must be a string"]                                                                                                                                
  }                                                                                                                                                                  
}

This separates the property path from the message content, so custom messages are preserved as-is.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

This approach is consistent with how other frameworks like Laravel and express-validator handle nested validation errors. The new groupValidationErrors method is
added alongside the existing flattenValidationErrors, and the choice is made in createExceptionFactory based on the errorFormat option.

@coveralls
Copy link

coveralls commented Feb 9, 2026

Pull Request Test Coverage Report for Build 23640676-3732-41c7-a8b9-81f1031f5729

Details

  • 12 of 12 (100.0%) changed or added relevant lines in 1 file are covered.
  • 50 unchanged lines in 5 files lost coverage.
  • Overall coverage decreased (-0.06%) to 89.77%

Files with Coverage Reduction New Missed Lines %
packages/microservices/errors/max-packet-length-exceeded.exception.ts 1 50.0%
packages/microservices/helpers/json-socket.ts 3 93.18%
packages/microservices/client/client-tcp.ts 12 85.06%
packages/microservices/server/server-mqtt.ts 16 83.04%
packages/microservices/server/server-tcp.ts 18 75.0%
Totals Coverage Status
Change from base Build a37b4272-bd8e-4a29-b752-179f6dce50e3: -0.06%
Covered Lines: 7459
Relevant Lines: 8309

💛 - Coveralls

@ponez
Copy link

ponez commented Feb 9, 2026

In grouped mode, message becomes Record<string, string[]> instead of string[]. This could catch people off guard if they have exception filters or interceptors that assume message is always an array. Might be worth mentioning this in the JSDoc on the errorFormat option so it's clear at the usage level.

Since #16268 is specifically about custom message options in decorators getting mangled, it'd be nice to see an explicit test with @IsNotEmpty({ message: 'Name is required' }) confirming the grouped output preserves user-defined messages as-is.

The checklist mentions docs were updated, but NestJS docs live in nestjs/docs.nestjs.com. This would need a companion PR there to add errorFormat to the Validation techniques page. I can open that docs PR if you'd like — happy to help get this across the finish line.

Add a new `errorFormat` option to `ValidationPipeOptions` that allows
users to choose between two validation error formats:

- 'list' (default): Returns an array of error message strings with
  parent path prepended to messages (current behavior)
- 'grouped': Returns an object with property paths as keys and arrays
  of unmodified constraint messages as values

The 'grouped' format separates property paths from error messages,
which prevents custom validation messages from being modified with
parent path prefixes.

Closes nestjs#16268
@at7211
Copy link
Contributor Author

at7211 commented Feb 9, 2026

@ponez
Thanks for the thorough review and suggestion!

I updated the JSDoc warning, and added the custom message test, also open the Docs PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants