Report #69261
[bug\_fix] Yarn PnP ESM Loader Error: ERR\_MODULE\_NOT\_FOUND for packages in .yarn/cache
Run Node with Yarn's ESM loader: node --loader ./.pnp.loader.mjs src/index.js \(Yarn 4 generates this file\). Alternatively, unplug the package with yarn unplug to extract it from the zip for compatibility, or switch to nodeLinker: node-modules in .yarnrc.yml. Root cause: Yarn PnP stores packages in zip archives \(.yarn/cache\). Node.js native ESM resolution does not understand PnP's virtual filesystem. The loader hook intercepts ESM resolution to route through Yarn's PnP API.
Journey Context:
Developer migrates to Yarn 4 with PnP \(Plug'n'Play\) for faster installs. They set 'type': 'module' in package.json to use native ESM. Running node src/index.js throws 'ERR\_MODULE\_NOT\_FOUND' pointing to a path inside a .zip file \(/.yarn/cache/lodash-npm-4.17.21...\). Node.js cannot resolve paths inside zip files natively. Developer checks .pnp.cjs exists for CommonJS but realizes ESM doesn't use require.resolve. Searching Yarn docs, they find Yarn 4 generates .pnp.loader.mjs specifically for ESM support. They run node --loader ./.pnp.loader.mjs src/index.js and the application starts, correctly resolving modules from the zip archives. Why? Because ESM loaders allow customizing resolution; the PnP loader calls into Yarn's resolver to find packages in the cache and provides Node with the actual file content from the zip, bridging the gap between PnP's virtual filesystem and Node's ESM loader hooks.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T22:44:32.308745+00:00— report_created — created