[Bug?]: Yarn PnP + monorepo breaks `instanceof` checks on errors from a shared package
Self-service
- [ ] I'd be willing to implement a fix
Describe the bug
When a TypeORM error is thrown from a dependency package in a monorepo, checks such as err instanceof QueryFailedError in other packages fail if PnP is enabled.
To reproduce
~~(I encountered this issue using TypeORM, so the steps involve spinning up a MySQL server first. I'll see if I can find an easier way to reproduce it.)~~ Switched to SQLite, no longer needs a MySQL server.
- Clone https://github.com/Frederick888/playground/tree/typeorm-monorepo
yarn installyarn buildyarn bar:start
When using PnP, the output is (errIsQueryFailedError is true in the shared/ package but false in bar/):
{
location: 'shared',
errIsQueryFailedError: true,
err: QueryFailedError: SQL driver error
at ChildClass.doThrow (/home/frederick/Programming/Others/playground/packages/shared/dist/src/index.js:41:19)
at main (/home/frederick/Programming/Others/playground/packages/bar/dist/index.js:8:38) {
query: 'SELECT FOO',
parameters: [ 1, 2, 3 ],
driverError: Error: SQL driver error
at ChildClass.doThrow (/home/frederick/Programming/Others/playground/packages/shared/dist/src/index.js:41:75)
at main (/home/frederick/Programming/Others/playground/packages/bar/dist/index.js:8:38)
}
}
{
location: 'bar',
errIsQueryFailedError: false,
err: QueryFailedError: SQL driver error
at ChildClass.doThrow (/home/frederick/Programming/Others/playground/packages/shared/dist/src/index.js:41:19)
at main (/home/frederick/Programming/Others/playground/packages/bar/dist/index.js:8:38) {
query: 'SELECT FOO',
parameters: [ 1, 2, 3 ],
driverError: Error: SQL driver error
at ChildClass.doThrow (/home/frederick/Programming/Others/playground/packages/shared/dist/src/index.js:41:75)
at main (/home/frederick/Programming/Others/playground/packages/bar/dist/index.js:8:38)
}
}
When using nodeLinker: node-modules, the output becomes (both errIsQueryFailedError are true):
{
location: 'shared',
errIsQueryFailedError: true,
err: QueryFailedError: SQL driver error
at ChildClass.doThrow (/home/frederick/Programming/Others/playground/packages/shared/dist/src/index.js:41:19)
at main (/home/frederick/Programming/Others/playground/packages/bar/dist/index.js:8:38) {
query: 'SELECT FOO',
parameters: [ 1, 2, 3 ],
driverError: Error: SQL driver error
at ChildClass.doThrow (/home/frederick/Programming/Others/playground/packages/shared/dist/src/index.js:41:75)
at main (/home/frederick/Programming/Others/playground/packages/bar/dist/index.js:8:38)
}
}
{
location: 'bar',
errIsQueryFailedError: true,
err: QueryFailedError: SQL driver error
at ChildClass.doThrow (/home/frederick/Programming/Others/playground/packages/shared/dist/src/index.js:41:19)
at main (/home/frederick/Programming/Others/playground/packages/bar/dist/index.js:8:38) {
query: 'SELECT FOO',
parameters: [ 1, 2, 3 ],
driverError: Error: SQL driver error
at ChildClass.doThrow (/home/frederick/Programming/Others/playground/packages/shared/dist/src/index.js:41:75)
at main (/home/frederick/Programming/Others/playground/packages/bar/dist/index.js:8:38)
}
}
Environment
System:
OS: Linux 6.12 Arch Linux
CPU: (20) x64 12th Gen Intel(R) Core(TM) i9-12900H
Binaries:
Node: 24.2.0 - /tmp/xfs-318ae311/node
Yarn: 4.9.2 - /tmp/xfs-318ae311/yarn
npm: 11.4.2 - /usr/bin/npm
bun: 1.2.16 - /usr/bin/bun
Additional context
Yarn version: 4.9.2
The issue can be fixed by re-exporting the types from shared/:
diff --git a/packages/bar/src/index.ts b/packages/bar/src/index.ts
index d04215d..61b5286 100644
--- a/packages/bar/src/index.ts
+++ b/packages/bar/src/index.ts
@@ -1,6 +1,5 @@
-import { AppDataSource, FooRepository } from "@playground/shared"
-import { QueryFailedError } from "typeorm"
+import { AppDataSource, FooRepository, QueryFailedError } from "@playground/shared"
async function main() {
await AppDataSource.initialize()
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index aea52f0..8292ec6 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -37,5 +37,5 @@ export const FooRepository = AppDataSource.getRepository(Foo).extend({
throw err
}
}
})
-
+export { QueryFailedError }
Can't check right now, but I imagine they are missing a peer dependency, allowing packages to be duplicated between your dependencies and theirs.
Can't check right now, but I imagine they are missing a peer dependency, allowing packages to be duplicated between your dependencies and theirs.
Yes, I can confirm moving typeorm to peerDependencies also fixed the issue (patch at the end).
I have to say this behaviour is quite error-prone. In my real project 'bar' is an API server and I just wanted to add another error handler rule on the QueryFailedError type. Originally typeorm was only a dependency of shared/. There are also other packages in my real monorepo that depends on shared/ but don't need the types from typeorm.
I guess logically it makes sense. But when I saw my unit tests passing (using new QueryFailedError(...)) but integration tests failing, it was quite an 'am I losing it' moment...
Is there any way to avoid duplicating identical dependencies?
$ y why -R typeorm
├─ @playground/bar@workspace:packages/bar
│ ├─ @playground/shared@workspace:packages/shared (via workspace:^)
│ └─ typeorm@npm:0.3.24 [c7eda] (via npm:^0.3.24 [c7eda])
│
└─ @playground/shared@workspace:packages/shared
└─ typeorm@npm:0.3.24 [da905] (via npm:^0.3.24 [da905])
diff --git a/packages/bar/package.json b/packages/bar/package.json
index db7b6f0..5ca130a 100644
--- a/packages/bar/package.json
+++ b/packages/bar/package.json
@@ -3,8 +3,9 @@
"version": "1.0.0",
"dependencies": {
"@playground/shared": "workspace:^",
"reflect-metadata": "^0.2.2",
+ "sqlite3": "^5.1.7",
"typeorm": "^0.3.24"
},
"main": "dist/index.js",
"private": true,
diff --git a/packages/shared/package.json b/packages/shared/package.json
index ca167c4..d83872d 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -14,9 +14,11 @@
"tsc-alias": "^1.8.10"
},
"dependencies": {
"@types/node": "^24.0.3",
- "reflect-metadata": "^0.2.2",
+ "reflect-metadata": "^0.2.2"
+ },
+ "peerDependencies": {
"sqlite3": "^5.1.7",
"typeorm": "^0.3.24"
}
}
diff --git a/yarn.lock b/yarn.lock
index c009542..46fb300 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -107,8 +107,9 @@ __metadata:
resolution: "@playground/bar@workspace:packages/bar"
dependencies:
"@playground/shared": "workspace:^"
reflect-metadata: "npm:^0.2.2"
+ sqlite3: "npm:^5.1.7"
tsc-alias: "npm:^1.8.10"
typeorm: "npm:^0.3.24"
languageName: unknown
linkType: soft
@@ -118,11 +119,12 @@ __metadata:
resolution: "@playground/shared@workspace:packages/shared"
dependencies:
"@types/node": "npm:^24.0.3"
reflect-metadata: "npm:^0.2.2"
- sqlite3: "npm:^5.1.7"
tsc-alias: "npm:^1.8.10"
- typeorm: "npm:^0.3.24"
+ peerDependencies:
+ sqlite3: ^5.1.7
+ typeorm: ^0.3.24
languageName: unknown
linkType: soft
"@sqltools/formatter@npm:^1.2.5":
Is there any way to avoid duplicating identical dependencies?
peerDependency is the way to do that. It is the only thing that tell package managers to enforce using the same package instance in two places in the dependency tree.
Regular dependency never enforces package identity. Any observed deduplication is not guaranteed and should not be relied upon
Thanks for the explanation 👍