-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Closed
Labels
Description
Provide environment information
System:
OS: macOS 26.3.1
CPU: (11) arm64 Apple M3 Pro
Memory: 2.63 GB / 36.00 GB
Binaries:
Node: 24.14.0 - /Users/<user>/.nvm/versions/node/v24.14.0/bin/node
npm: 11.9.0 - /Users/<user>/.nvm/versions/node/v24.14.0/bin/npm
pnpm: 10.32.1 - /opt/homebrew/bin/pnpm
Browsers:
Chrome: 146.0.7680.153
Firefox Developer Edition: 149.0
Safari: 26.3.1
npmPackages:
@trpc/server: ^11.13.4 => 11.13.4
typescript: ^5.9.3 => 5.9.3
Describe the bug
Problem
While using Node's VM, errors that are implicitly thrown by the JavaScript engine (e.g. TypeError) use VM's own internal built-in constructors, meaning instanceof checks for Error will fail because the constructors are different:
// packages/server/src/unstable-core-do-not-import/error/TRPCError.ts
export function getCauseFromUnknown(cause: unknown): Error | undefined {
if (cause instanceof Error) { // 👈 returns false
return cause;
}
...
}Because of this, tRPC ends up throwing an error without a message:
{
"error": {
"message": "",
"code": -32603,
"data": {
"code": "INTERNAL_SERVER_ERROR",
"httpStatus": 500,
"stack": "TRPCError: \n at getTRPCErrorFromUnknown ...",
"path": "greet"
}
}
}Link to reproduction
https://github.com/znikola/trpc-error-handling-reproduction
To reproduce
Since Error's message and stack properties are non-enumerable, they won't be copied by Object.assign().
// packages/server/src/unstable-core-do-not-import/error/TRPCError.ts
export function getCauseFromUnknown(cause: unknown): Error | undefined {
...
// If it's an object, we'll create a synthetic error
if (isObject(cause)) {
return Object.assign(new UnknownCauseError(), cause); // 👈 message and stack won't be copied
}This is the proposed change to the UnknownCauseError:
// packages/server/src/unstable-core-do-not-import/error/TRPCError.ts
class UnknownCauseError extends Error {
[key: string]: unknown;
constructor(cause: object) {
super(getMessage(cause));
Object.assign(this, cause);
}
}
function getMessage(cause: object) {
return (cause as Error).message;
}and throw it like this:
// packages/server/src/unstable-core-do-not-import/error/TRPCError.ts
export function getCauseFromUnknown(cause: unknown): Error | undefined {
...
// If it's an object, we'll create a synthetic error
if (isObject(cause)) {
return new UnknownCauseError(cause);
}
return undefined;
}This was verified by patching the getCauseFromUnknown() function in the latest version of @trpc/server (11.13.4) in the node_modules:
// node_modules/@trpc/server/dist/tracked-Bjtgv3wJ.mjs
...
var UnknownCauseError = class extends Error {
constructor(cause) {
super(getMessage(cause));
Object.assign(this, cause);
}
};
function getMessage(cause) {
return (cause).message;
}
function getCauseFromUnknown(cause) {
...
// 👇 old
// if (isObject(cause)) return Object.assign(new UnknownCauseError(), cause);
// 👇 new
if (isObject(cause)) return new UnknownCauseError(cause);
return void 0;
}
...Additional information
No response
👨👧👦 Contributing
- 🙋♂️ Yes, I'd be down to file a PR fixing this bug!
Reactions are currently unavailable