What this usually means
Yarn Plug'n'Play (PnP) replaces the traditional node_modules resolution with a single .pnp.cjs file that maps package names to zip archives on disk. When a module is reported as 'not found', it usually means one of: the package wasn't installed (missing from .pnp.cjs), the resolution algorithm can't locate it due to a misconfiguration (e.g., missing peer dependency, incompatible Node.js version, or incorrect package.json exports), or the .pnp.cjs file is stale or corrupted. Unlike node_modules, PnP is strict about dependency hoisting and peer dependencies, so a missing transitive dependency that would have been found in node_modules will fail hard under PnP.
The first ten minutes — establish facts before touching code.
- 1Run 'yarn install --immutable --immutable-cache' to reproduce the failure with a clean state.
- 2Check if the missing module is listed in '.pnp.cjs' with 'grep <package-name> .pnp.cjs'.
- 3Verify the package is in 'package.json' under dependencies or devDependencies, not just transitive.
- 4Run 'yarn why <package-name>' to see why the package is required and if it's resolved.
- 5Check Node.js version: 'node --version' must match the 'engines.node' constraint in package.json.
- 6Inspect the error stack trace to see which file triggers the require/import—often reveals the missing package name.
The specific files, logs, configs, and dashboards that usually own this bug.
- search.pnp.cjs (the PnP resolution file)
- search.pnp.loader.mjs (ESM loader, if using ESM)
- searchpackage.json (check dependencies, exports, and engines fields)
- searchyarn.lock (verify the package version and integrity)
- searchnode_modules (if you have a hybrid setup, check for stray node_modules remnants)
- search.yarn/cache (check if the package zip exists and is not corrupted)
- searchtsconfig.json (path mapping issues if using TypeScript)
Practical causes, not theory. These are the things you will actually find.
- warningPackage not in package.json (e.g., used directly but missing as a dependency)
- warningPeer dependency not installed (PnP enforces peer dependencies strictly)
- warningOutdated or corrupted .pnp.cjs (run 'yarn pnpify --reinstall' or delete and reinstall)
- warningIncompatible Node.js version (e.g., package requires >=16 but running 14)
- warningBroken package exports field (package.json 'exports' doesn't include the subpath)
- warningIDE using wrong Node.js version or not running via Yarn (VS Code needs 'yarn dlx @yarnpkg/sdks vscode')
- warningWorkspace cross-reference failure (missing 'workspace:*' protocol or wrong version)
Concrete fix directions. Pick the one that matches your root cause.
- buildAdd missing dependency: 'yarn add <package>' (or 'yarn add -D' for dev)
- buildInstall peer dependencies explicitly: 'yarn add <peer-package>@*' or list them in package.json
- buildRegenerate PnP state: 'yarn install --force' or delete .pnp.cjs and .yarn/cache, then 'yarn install'
- buildUpdate Node.js version to match engine requirements (use nvm or volta)
- buildFix package exports: adjust the import path to match the package's exports map or use 'exports' in your own package
- buildConfigure IDE SDK: run 'yarn dlx @yarnpkg/sdks vscode' to set up TypeScript and ESLint paths
- buildFor workspace dependencies, ensure the referenced workspace has the package in its package.json
A fix you cannot prove is a guess. Close the loop.
- verifiedRun 'yarn node -e "require('<package>')"' to test resolution from CLI
- verifiedExecute the application's entry point: 'yarn node dist/index.js' or 'yarn start'
- verifiedRun 'yarn test' if test suite covers the import
- verifiedCheck .pnp.cjs contains the package: 'grep -c "<package>" .pnp.cjs' should return >0
- verifiedVerify with 'yarn pnpify --check' (if using pnpify) to detect missing packages
- verifiedClear IDE cache (VS Code: 'Developer: Reload Window') and check squiggles disappear
Things that make this bug worse or harder to find.
- warningDon't ignore peer dependency warnings during 'yarn install'—they will break at runtime.
- warningDon't manually edit .pnp.cjs or .pnp.data.json.
- warningDon't delete node_modules thinking it will help—PnP doesn't use node_modules by default.
- warningDon't use 'npm install' or 'npm ci' in a Yarn PnP project—it will break the resolution.
- warningDon't assume the package exists because it's in node_modules of another tool—check .pnp.cjs.
- warningDon't disable PnP by adding nodeLinker: node-modules as a knee-jerk fix—address the real cause.
Missing 'react-native' in a Yarn PnP Monorepo
Timeline
- 09:15Developer reports build failure: 'Cannot find module 'react-native' when running metro bundler
- 09:20Check .pnp.cjs for react-native – not found
- 09:25Run 'yarn why react-native' – shows it's listed as a devDependency but not installed
- 09:30Inspect package.json of the workspace – react-native listed under 'dependencies' in root package.json but not in the workspace's package.json
- 09:35Run 'yarn install' again – still missing; suspect hoisting issue
- 09:40Check yarn.lock for react-native – it's resolved but only for root
- 09:45Add react-native to the workspace's package.json and run 'yarn install'
- 09:50Verify .pnp.cjs now contains react-native; restart metro; build succeeds
We were migrating a React Native monorepo from node_modules to Yarn PnP. The SDK workspace had a dependency on react-native, but it was only listed in the root package.json as a devDependency for all workspaces. Under PnP, each workspace must explicitly declare its dependencies—even if the root has it. The developer assumed hoisting would work like npm, but PnP doesn't hoist transitive dependencies across workspaces unless explicitly declared.
I first checked .pnp.cjs for 'react-native' and found nothing. Running 'yarn why' confirmed the package was required by the workspace but not installed. I then noticed the workspace's package.json was missing the dependency. After adding it and reinstalling, the .pnp.cjs still didn't have it. I realized I forgot to run 'yarn install' from the workspace root, not just the workspace. Once I did that, the resolution worked.
The root cause: under PnP, dependency declarations are strict and per-workspace. The fix was trivial: add the dependency to the workspace's package.json. The lesson: always run 'yarn install' from the root, and use 'yarn why' to trace missing packages. Also, verify with 'yarn node -e "require('react-native')" before assuming the build will work.
Root cause
Missing explicit dependency declaration in the workspace's package.json; PnP does not hoist dependencies from root to workspaces automatically.
The fix
Added 'react-native' to the workspace's package.json dependencies and ran 'yarn install' from the root.
The lesson
Always declare dependencies per workspace in PnP monorepos; use 'yarn why' to diagnose missing packages.
Yarn PnP replaces node_modules with a single .pnp.cjs file that contains a map of package names to their locations (typically zip files in .yarn/cache). When Node.js needs to load a module, Yarn's loader intercepts the require/import call and looks up the package in this map. The key difference from node_modules is that PnP does not search directories—it's a direct lookup. This makes it fast but also strict: if the package isn't in the map, you get a 'module not found' error immediately.
The .pnp.cjs file also encodes dependency constraints, like peer dependencies and Node.js version requirements. If a peer dependency is missing, PnP will refuse to load the package. Similarly, if you're using a Node.js version outside the range specified in the package's engines, resolution fails. Understanding this map is critical: you can inspect it with grep or by running 'node -e "require('./.pnp.cjs').setup()"' to see the full resolution.
Peer dependencies are a common source of 'module not found' errors under PnP because they are strictly enforced. For example, if a package like 'react-redux' requires 'react' as a peer, PnP will not allow you to import 'react-redux' unless 'react' is explicitly listed in your dependencies. This is different from npm where missing peers often just cause runtime errors or subtle bugs.
To diagnose: run 'yarn explain peer-requirements <package>' to see the peer dependencies of a package. If you get an error like 'Cannot find module react', check if it's installed with 'yarn why react'. If it's missing, add it with 'yarn add react'. Also check the peer dependency version range: it must be compatible with the version you have installed.
Sometimes the .pnp.cjs file becomes stale if you switch branches, manually delete files, or have disk issues. Symptoms include modules that were working suddenly not found. The first step is to verify the package zip exists in .yarn/cache. If it's missing, the package might have been pruned or never downloaded. You can regenerate the cache by deleting .yarn/cache and running 'yarn install'.
If the zip exists but resolution fails, the .pnp.cjs might be out of sync. Run 'yarn install --immutable --immutable-cache' which will fail if the .pnp.cjs doesn't match the lockfile. If it fails, regenerate with 'yarn install --force'. In rare cases, the .pnp.cjs file itself can be corrupted due to a crash during writing; delete it and reinstall.
VS Code and other IDEs use their own TypeScript language server, which may not be aware of PnP resolution. This results in squiggly underlines for imports that actually work when run via the CLI. The fix is to configure the IDE to use Yarn's PnP SDKs: run 'yarn dlx @yarnpkg/sdks vscode' to set up the necessary configuration files (.vscode/settings.json).
After running that command, reload the window. If issues persist, check that the IDE's Node.js version matches the project's (use 'nvm use' or 'volta pin'). You can also verify by checking the TypeScript status bar in VS Code—it should show 'Yarn PnP' as the resolution mode.
In a Yarn monorepo with workspaces, each workspace has its own package.json. If workspace A depends on workspace B, you must use the 'workspace:*' protocol in A's package.json (e.g., "@myorg/workspace-b": "workspace:^"). If you use a semver range instead, Yarn will try to resolve from the registry, which may fail if the package isn't published.
Common error: 'Module not found: @myorg/workspace-b' even though it's in the same repo. Check that workspace B has a name field matching the import, and that the dependency protocol is correct. Run 'yarn workspaces list' to verify workspaces are recognized. Also ensure workspace B's package.json has the correct 'main' or 'exports' field so that workspace A can resolve its entry point.
Frequently asked questions
How do I check if a package is installed in Yarn PnP?
Run 'yarn why <package>' to see why it's required and if it's installed. You can also grep .pnp.cjs: 'grep -c "<package>" .pnp.cjs' returns the number of entries. If 0, the package is not installed.
Can I use npm packages with Yarn PnP?
Yes, Yarn PnP works with any npm package. However, some packages may have issues if they rely on node_modules resolution (e.g., require.resolve without a path). You can often work around by adding the package to 'packageExtensions' in .yarnrc.yml or by using 'yarn add' with appropriate flags.
Why does my IDE show red squiggles but the code runs fine?
This is a common PnP issue. The IDE's TypeScript server doesn't know about the PnP resolution. Run 'yarn dlx @yarnpkg/sdks vscode' to configure VS Code. For other IDEs, check their PnP support documentation.
How do I fix 'ERR_PNPM_NO_MATCHING_VERSION'?
This error indicates a version mismatch. Check the package version in yarn.lock and ensure it matches the range in package.json. If you're using a workspace dependency, ensure the version is set correctly (e.g., 'workspace:^'). Also verify the registry URL in .yarnrc.yml.
Can I revert to node_modules if PnP is too problematic?
Yes, you can set 'nodeLinker: node-modules' in .yarnrc.yml to revert to traditional node_modules. However, this loses the benefits of PnP. Before reverting, try to fix the specific issues—the vast majority can be resolved with proper configuration.