fix: use instanceof instead of constructor name in formatErrors#16089
Merged
paulpopus merged 5 commits intopayloadcms:mainfrom Apr 13, 2026
Merged
fix: use instanceof instead of constructor name in formatErrors#16089paulpopus merged 5 commits intopayloadcms:mainfrom
paulpopus merged 5 commits intopayloadcms:mainfrom
Conversation
Replace constructor name string comparison with instanceof checks for APIError and ValidationError, preventing production builds from silently dropping the error data field when class names are minified.
paulpopus
approved these changes
Apr 13, 2026
Contributor
|
🚀 This is included in version v3.83.0 |
milamer
pushed a commit
to milamer/payload
that referenced
this pull request
Apr 20, 2026
…oadcms#16089) ## What Fixes `formatErrors` silently dropping the `data` field from `APIError` responses in production builds. Closes payloadcms#16050 ## Why `formatErrors` used `proto.constructor.name` string comparison to identify `APIError` and `ValidationError` instances. In production, webpack/terser minifies class names (e.g., `APIError` → `t`), causing the check to fail. This silently drops `error.data`, breaking any feature that relies on it — for example, 2FA flows that check `data.requires2FA`. The `APIErrorName`/`ValidationErrorName` module variables were designed to handle this via dynamic reassignment in constructors, but minifiers can inline the initial string value at import sites, defeating the mechanism. ## Changes - Replaced `proto.constructor.name === APIErrorName` / `ValidationErrorName` checks with `instanceof APIError` / `instanceof ValidationError` - For the Mongoose `ValidationError` fallback (which isn't a Payload class), switched to checking `incoming.name === 'ValidationError'` — a string property that Mongoose sets explicitly and isn't affected by minification - Removed unused `APIErrorName` / `ValidationErrorName` imports ## Why instanceof is safe here The original code avoided `instanceof` citing [TypeScript#13965](microsoft/TypeScript#13965), where `instanceof` fails for classes extending `Error` when transpiled to ES5. However: - Payload's `tsconfig` targets **ES2022**, so native class syntax is preserved - `instanceof` works correctly with ES2015+ classes extending `Error` - The original workaround itself was broken by minification — a worse failure mode ## Testing - `npx tsc --noEmit` passes with no errors - Verified that Payload's `ValidationError` (which has `data`) is caught by the `instanceof` branch and never falls through to the Mongoose branch - The Mongoose `ValidationError` check uses the `name` property which Mongoose sets explicitly (`this.name = 'ValidationError'`) — not affected by minification --------- Co-authored-by: Paul Popus <paul@payloadcms.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes
formatErrorssilently dropping thedatafield fromAPIErrorresponses in production builds.Closes #16050
Why
formatErrorsusedproto.constructor.namestring comparison to identifyAPIErrorandValidationErrorinstances. In production, webpack/terser minifies class names (e.g.,APIError→t), causing the check to fail. This silently dropserror.data, breaking any feature that relies on it — for example, 2FA flows that checkdata.requires2FA.The
APIErrorName/ValidationErrorNamemodule variables were designed to handle this via dynamic reassignment in constructors, but minifiers can inline the initial string value at import sites, defeating the mechanism.Changes
proto.constructor.name === APIErrorName/ValidationErrorNamechecks withinstanceof APIError/instanceof ValidationErrorValidationErrorfallback (which isn't a Payload class), switched to checkingincoming.name === 'ValidationError'— a string property that Mongoose sets explicitly and isn't affected by minificationAPIErrorName/ValidationErrorNameimportsWhy instanceof is safe here
The original code avoided
instanceofciting TypeScript#13965, whereinstanceoffails for classes extendingErrorwhen transpiled to ES5. However:tsconfigtargets ES2022, so native class syntax is preservedinstanceofworks correctly with ES2015+ classes extendingErrorTesting
npx tsc --noEmitpasses with no errorsValidationError(which hasdata) is caught by theinstanceofbranch and never falls through to the Mongoose branchValidationErrorcheck uses thenameproperty which Mongoose sets explicitly (this.name = 'ValidationError') — not affected by minification