Repository navigation
Description
Describe the bug
With PnP, when the global folder (default $HOME/.yarn/berry) is reached through a symlinked path, pnpapi.resolveRequest hands Node a realpath'd filename for a package served from an archive, while the PnP map records that archive under the symlinked spelling. A relative require from inside the archive is then resolved against the realpath and fails:
Error: Cannot find module './dep.cjs'
Require stack:
- /volume1/homes/<user>/.yarn/berry/cache/repro-pkg-file-c28e1974f5-10c0.zip/node_modules/repro-pkg/index.js
- /tmp/repro/consumer/main.js
at require$$0.Module._resolveFilename (.pnp.cjs:5924:54)
The file is in the archive — unzip -l lists node_modules/repro-pkg/dep.cjs — and a package subpath resolves fine: require('repro-pkg/dep.cjs') prints dep loaded. Only the entry's relative require('./dep.cjs') fails.
To reproduce
# $HOME here is /var/services/homes/<user>, a symlink to /volume1/homes/<user> (Synology DSM)
# a package whose entry requires a file beside it
mkdir -p /tmp/repro/stage/package /tmp/repro/consumer/.yarn/releases
cat > /tmp/repro/stage/package/package.json <<'JSON'
{ "name": "repro-pkg", "version": "1.0.0", "main": "index.js" }
JSON
echo "module.exports = require('./dep.cjs')" > /tmp/repro/stage/package/index.js
echo "module.exports = 'dep loaded'" > /tmp/repro/stage/package/dep.cjs
(cd /tmp/repro/stage && tar czf /tmp/repro/repro-pkg.tgz package)
# a PnP consumer that installs it from the tarball
cp /path/to/yarn-4.18.1.cjs /tmp/repro/consumer/.yarn/releases/
printf 'nodeLinker: pnp\nyarnPath: .yarn/releases/yarn-4.18.1.cjs\n' > /tmp/repro/consumer/.yarnrc.yml
cat > /tmp/repro/consumer/package.json <<'JSON'
{ "name": "consumer", "private": true, "dependencies": { "repro-pkg": "file:../repro-pkg.tgz" } }
JSON
echo "console.log(require('repro-pkg'))" > /tmp/repro/consumer/main.js
cd /tmp/repro/consumer
yarn install
yarn node main.jsThe two APIs disagree
$ node -r ./.pnp.cjs -e "const p=require('pnpapi');console.log('toUnqualified:',p.resolveToUnqualified('repro-pkg',process.cwd()+'/main.js'));console.log('request: ',p.resolveRequest('repro-pkg',process.cwd()+'/main.js'))"
toUnqualified: /var/services/homes/<user>/.yarn/berry/cache/repro-pkg-file-c28e1974f5-10c0.zip/node_modules/repro-pkg/
request: /volume1/homes/<user>/.yarn/berry/cache/repro-pkg-file-c28e1974f5-10c0.zip/node_modules/repro-pkg/index.js
Adding the realpath of that same folder makes it pass, with the same archive file:
# .yarnrc.yml
globalFolder: /volume1/homes/<user>/.yarn/berry # instead of the symlinked spelling$ yarn node main.js
dep loaded
Expected behaviour
dep loaded. A file inside an installed package should resolve regardless of whether an ancestor of the cache folder is a symlink.
Environment
- Yarn 4.18.1 (a project pinned to 4.15.0 fails the same way)
- Node 24.21.0
- Synology DSM:
$HOME=/var/services/homes/<user>→/volume1/homes/<user>. The same shape occurs on macOS (/var→/private/var) or any symlinked$HOME/global folder.
Notes
--preserve-symlinks/--preserve-symlinks-maindo not help: the realpath comes from Yarn's ownresolveRequest, so Node's symlink handling is never involved. (Applied globally they do break Yarn's own bootstrap:Cannot find module './lib/corepack.cjs'.)- Presumably the hook matches the request's parent filename against the recorded package location; a realpath'd parent no longer matches, so the relative request falls through to the default resolver, which cannot
stata path inside a zip. That would also explain why a package subpath works and a relative request does not. - Workarounds:
enableGlobalCache: falseputs the cache inside the project;globalFolderset to the realpath of the default keeps the shared cache (same zip, same hash). - Fix direction: don't return a realpath'd filename for a zip package — or match package locations after canonicalising both sides — when the recorded location is reached through a symlink.
- Related: fix(pnpapi): dedupe pnpapi instances where .pnp.cjs is a symlink #5674 (dedupe
pnpapiwhere.pnp.cjsis a symlink), [Bug?]: Global/Local Cache Not Shareable Between Workspaces #6049 (cache sharing), [Bug?]:globalFolderis not${HOME}/.yarn/berryon Windows as documented #6814 (globalFolderon Windows).
Activity
tarun5004 commentedon Oct 26, 2026
Hi, I'd like to help investigate this PnP resolution issue using Codex assistance. I'd start with a temporary symlinked globalFolder and a small local package whose entry requires a sibling file, then compare resolveToUnqualified and resolveRequest. I haven't run the reproduction yet. Is this still available for a community contributor? If it is, could you assign it to @tarun5004 and confirm the preferred fix direction before I change resolution behavior?