Repository navigation
feat(errors): Recognize Feathers errors across duplicate module copies - #3726
Open
fratzinger wants to merge 1 commit into
Open
fratzinger wants to merge 1 commit into
fratzinger wants to merge 1 commit into
Conversation
`instanceof FeathersError` fails when multiple versions of feathers are
installed or CommonJS and ES modules are mixed. Feathers errors are now
branded with a global `Symbol.for('feathers.error')` and the error
classes implement `Symbol.hasInstance`, so `instanceof` works across
module copies while still narrowing to the specific error type.
Also adds a typed `isFeathersError(error, type?)` guard that accepts an
error name or status code, and adds the missing `Gone` entries to
`errors` and the `Errors` interface.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
daffl
reviewed
Oct 28, 2026
| readonly data: any | ||
| readonly errors: any | ||
|
|
||
| static [Symbol.hasInstance]<T>(this: abstract new (...args: any[]) => T, value: unknown): value is T { |
Member
There was a problem hiding this comment.
I'm not sure I understand what this is for. From what I know Symbol.for already ensures that you get the same Symbol instance across the entire runtime. I thought just defining the property and checking if it exists should work.
This branch has not been deployed
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.
Summary
instanceof FeathersErrorfails when an error comes from a different copy offeathers(multiple installed versions, mixed CJS/ESM, other realms). Errors are now branded with a global symbol, and the error classes check that brand inSymbol.hasInstance.const FEATHERS_ERROR = Symbol.for('feathers.error') class FeathersError extends Error { + static [Symbol.hasInstance]<T>(this: abstract new (...args: any[]) => T, value: unknown): value is T + prototype chain matches → true + no string brand on value → false + this === FeathersError → true + this === built-in class for brand → true // NotFound only matches 'NotFound' constructor(...) ... + Object.defineProperty(this, FEATHERS_ERROR, { value: name }) // non-enumerable }Plus an explicit, typed guard:
~typestring: payloads can't spoof it, and it is not copied by spread/Object.assignor emitted bytoJSON.instanceofstill narrows to the specific class, including user subclasses. User subclasses only match by prototype chain, sonew BadRequest() instanceof MyErrorstaysfalse.isFeathersError.Goneentries toerrorsand theErrorsinterface. Before this,convert({ name: 'Gone' })returned a plainError.api/errors.md.Evidence
The new test loads a second module instance via
vi.resetModules():errors.tsfromv6):× recognizes errors from a duplicate module instance,AssertionError: The expression evaluated to a falsy valueAfter:
✓and the fullfeatherspackage suite passes (504 passed, 1 skipped)instanceof,isFeathersErrorby name and code,@ts-expect-errorfor unknown names and codes) was checked withtsc --noEmit.Merge Danger
Door: two-way
The change adds to the public API (
isFeathersError,errors.Gone). It is easy to revert as long as no one depends on it yet.Blast Radius: low
instanceofchecks against Feathers error classes can now returntruefor branded errors from another module copy. That is the intent. Behaviour for errors that were already recognized does not change. Errors from v5@feathersjs/errorshave no brand and are still not recognized.🤖 Generated with Claude Code