GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat(errors): Recognize Feathers errors across duplicate module copies by fratzinger · Pull Request #3726 · feathersjs/feathers

Conversation

@fratzinger

@fratzinger fratzinger commented Oct 28, 2026

Copy link
Copy Markdown
Member

Summary

instanceof FeathersError fails when an error comes from a different copy of feathers (multiple installed versions, mixed CJS/ESM, other realms). Errors are now branded with a global symbol, and the error classes check that brand in Symbol.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:

isFeathersError(error)             // error is FeathersError
isFeathersError(error, 'NotFound') // error is NotFound
isFeathersError(error, 404)        // error is NotFound
isFeathersError(error, 418)        // type error
  • Symbol instead of a ~type string: payloads can't spoof it, and it is not copied by spread/Object.assign or emitted by toJSON.
  • instanceof still narrows to the specific class, including user subclasses. User subclasses only match by prototype chain, so new BadRequest() instanceof MyError stays false.
  • The HTTP error middleware now uses isFeathersError.
  • Adds the missing Gone entries to errors and the Errors interface. Before this, convert({ name: 'Gone' }) returned a plain Error.
  • Docs: new "Checking errors" section in api/errors.md.

Evidence

The new test loads a second module instance via vi.resetModules():

const duplicate = await import('../src/errors.js')
const error = new duplicate.NotFound()

error instanceof errors.FeathersError      // true
error instanceof errors.NotFound           // true
error instanceof errors.BadRequest         // false
errors.isFeathersError(error, 404)         // true
  • Before (errors.ts from v6): × recognizes errors from a duplicate module instance, AssertionError: The expression evaluated to a falsy value
    After: ✓ and the full feathers package suite passes (504 passed, 1 skipped)
  • Type narrowing (instanceof, isFeathersError by name and code, @ts-expect-error for unknown names and codes) was checked with tsc --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

instanceof checks against Feathers error classes can now return true for branded errors from another module copy. That is the intent. Behaviour for errors that were already recognized does not change. Errors from v5 @feathersjs/errors have no brand and are still not recognized.

🤖 Generated with Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@fratzinger @claude
feat(errors): Recognize Feathers errors across duplicate module copies
d737b50
`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 <[email protected]>
daffl
daffl reviewed Oct 28, 2026
View reviewed changes
Comment thread packages/feathers/src/errors.ts
readonly data: any
readonly errors: any

static [Symbol.hasInstance]<T>(this: abstract new (...args: any[]) => T, value: unknown): value is T {

@daffl daffl Oct 28, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

This branch has not been deployed

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

Reviewers

@daffl daffl daffl left review comments

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@fratzinger @daffl
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.