Skip to content

fix: use instanceof instead of constructor name in formatErrors#16089

Merged
paulpopus merged 5 commits intopayloadcms:mainfrom
mahmoodhamdi:fix/format-errors-minification
Apr 13, 2026
Merged

fix: use instanceof instead of constructor name in formatErrors#16089
paulpopus merged 5 commits intopayloadcms:mainfrom
mahmoodhamdi:fix/format-errors-minification

Conversation

@mahmoodhamdi
Copy link
Copy Markdown
Contributor

What

Fixes formatErrors silently dropping the data field from APIError responses in production builds.

Closes #16050

Why

formatErrors used proto.constructor.name string comparison to identify APIError and ValidationError instances. In production, webpack/terser minifies class names (e.g., APIErrort), 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, 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

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 paulpopus merged commit 216d162 into payloadcms:main Apr 13, 2026
322 of 324 checks passed
@github-actions
Copy link
Copy Markdown
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: formatErrors drops data field from APIError in production builds due to class name minification

2 participants