Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/utils' is not defined by "exports" when you import a deep subpath that is not listed in the package's "exports" map in package.json. The fastest fix is to import only the documented public API (e.g. import { v4 as uuidv4 } from 'uuid' instead of import uuidv4 from 'uuid/v4'). If you own the package, add the missing subpath to "exports". If the error appeared after an upgrade, try a clean reinstall or pin to the previous version.
Exact Error Messages You Will See
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/utils' is not defined by "exports" in /project/node_modules/some-package/package.json
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/parser' is not defined by "exports" in /project/node_modules/some-package/package.json
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath 'v4' is not defined by "exports" in /project/node_modules/uuid/package.json
Full Error Stack Trace
node:internal/modules/esm/resolve:265
throw new ERR_PACKAGE_PATH_NOT_EXPORTED(pkgPath, subpath, base);
^
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/utils' is not defined
by "exports" in /project/node_modules/some-package/package.json
at exportsNotExported (node:internal/modules/esm/resolve:265:13)
at packageExportsResolve (node:internal/modules/esm/resolve:591:13)
at resolveExports (node:internal/modules/cjs/loader:591:36)
at Module._findPath (node:internal/modules/cjs/loader:688:31)
at Module._resolveFilename (node:internal/modules/cjs/loader:1130:27)
at Object.<anonymous> (/project/index.js:1:1) {
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}
What is ERR_PACKAGE_PATH_NOT_EXPORTED?
ERR_PACKAGE_PATH_NOT_EXPORTED is a Node.js runtime error thrown when your
code attempts to import or require() a file path inside an npm package that the
package intentionally does not expose. Node.js enforces the
"exports" map
in package.json strictly: any subpath not listed there is treated as private and
inaccessible — even if the file physically exists on disk.
This is a deliberate encapsulation mechanism. Package authors use it to define a stable public API
and prevent consumers from depending on internal implementation paths. When a package adds or updates its
"exports" field, previously reachable deep imports suddenly fail with this error.
Example: package that triggers the error
// node_modules/some-package/package.json
{
"name": "some-package",
"exports": {
".": "./dist/index.js"
}
}
// your code – this BREAKS
import { helper } from 'some-package/lib/utils';
// → Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/utils' is not defined by "exports"
// your code – this WORKS
import { helper } from 'some-package';
// → resolves to ./dist/index.js ✓
Common Causes Table
| Situation | Why it happens | Fix |
|---|---|---|
Importing uuid/v4, uuid/v1 |
uuid v8+ replaced deep subpath imports with named exports in the root module | import { v4 as uuidv4 } from 'uuid' |
Package upgrade added strict exports map |
A minor or patch version added an exports field that omits your import path |
Pin older version; migrate to public API; or patch the dependency |
Importing package/lib/… or package/src/… |
Internal paths are not listed in exports; only the public surface is |
Use the documented public API or wildcard export pattern in your own package |
| Conditional exports mismatch (no matching condition) | The environment doesn't match any condition key and no "default" is defined |
Add "default" fallback; or set the correct --conditions flag |
Exports entry explicitly set to null |
Package author intentionally blocked the path: "./internal/*": null |
Use only the public API; no workaround without patching the package |
| Jest or older tooling resolving paths directly | Jest < v28 does not honour the exports field; resolves by file path instead |
Upgrade Jest to v28+; use moduleNameMapper for affected packages |
| Monorepo workspace importing sibling package internals | Workspace symlinks mean Node.js enforces the sibling's exports map strictly |
Add required subpaths to the sibling package's exports; or build before running |
Accessing package/package.json |
Some tools read package.json via a subpath import; must be explicitly exported |
Add "./package.json": "./package.json" to the exports map |
Stale or corrupted node_modules |
Mixed package versions from incomplete installs expose wrong exports maps |
Delete node_modules and package-lock.json, then npm install |
TypeScript moduleResolution: node with modern packages |
Old resolution mode ignores exports; emitted JS then fails at runtime |
Set "moduleResolution": "bundler" or "node16" in tsconfig.json |
How to Fix ERR_PACKAGE_PATH_NOT_EXPORTED
Fix 1 – Use only the package's public API (most common fix)
Read the error message to get the package name and subpath. Then check the package's
README, changelog, or the exports field in
node_modules/<package>/package.json for the correct replacement.
// uuid – the most frequent real-world case
// Before (broken since uuid v8)
import uuidv4 from 'uuid/v4';
// After (ESM)
import { v4 as uuidv4 } from 'uuid';
// After (CommonJS)
const { v4: uuidv4 } = require('uuid');
// Generic deep import → root import
// Before (broken)
import { helper } from 'some-package/lib/utils';
// After (correct – use the documented export)
import { helper } from 'some-package';
// or the documented named subpath:
import { helper } from 'some-package/utils';
Fix 2 – Add the missing export (if you own the package)
Open your package's package.json and add the subpath to the "exports" field.
Use wildcard patterns to expose whole directories.
// package.json – add any subpath you need to make importable
{
"name": "my-package",
"exports": {
".": "./dist/index.js",
"./utils": "./dist/utils.js",
"./models/*": "./dist/models/*.js",
"./package.json": "./package.json"
}
}
"./lib/*": "./lib/*" as a blanket wildcard exposes
all internal files publicly. Only export paths that are part of your intended public API.
Mark paths that must stay private with null: "./internal/*": null.
Fix 3 – Backwards-compatible exports (for package authors)
When you add an exports field for the first time, all existing deep imports
instantly break for consumers unless you explicitly list them. Use this pattern to make the
addition non-breaking:
{
"main": "./lib/index.js",
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/index": "./lib/index.js",
"./lib/index.js": "./lib/index.js",
"./package.json": "./package.json",
"./lib/*": "./lib/*.js",
"./lib/*.js": "./lib/*.js"
}
}
Fix 4 – Pin to an older version
If the package added the exports map in a recent release and broke your import,
pin to the last version that did not have it while you migrate:
npm install some-package@2.3.0
Check the package's GitHub releases or CHANGELOG.md to identify which version
introduced the exports field.
Fix 5 – Clean reinstall (fix stale node_modules)
A corrupted or mixed-version node_modules tree can expose an older
package.json that has a different exports map. A clean reinstall
resolves this:
# macOS / Linux
rm -rf node_modules
rm -f package-lock.json
npm cache clean --force
npm install
# Windows (Command Prompt)
rd /s /q node_modules
del package-lock.json
npm cache clean --force
npm install
Fix 6 – Patch with patch-package
If you cannot change your import path and cannot upgrade, use
patch-package
to add the missing export to the dependency's package.json without changing
your import path:
# 1. Manually edit node_modules/some-package/package.json to add the missing subpath
# 2. Create the patch file
npx patch-package some-package
# 3. Apply patches automatically after every npm install (add to package.json)
# "scripts": { "postinstall": "patch-package" }
Fix 7 – Alias via bundler (webpack / Vite / esbuild)
Map the private path to a public equivalent inside your bundler config instead of importing the private path directly.
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
resolve: {
alias: {
'some-package/lib/utils': 'some-package/utils'
}
}
});
// webpack.config.js
module.exports = {
resolve: {
alias: {
'some-package/lib/utils': require.resolve('some-package/utils')
}
}
};
Fix 8 – Fix Jest (v28+ or moduleNameMapper)
Jest versions before 28 do not honour the exports field. Upgrade to Jest 28+
for native support. For per-package workarounds, add a moduleNameMapper:
// jest.config.js
module.exports = {
moduleNameMapper: {
// Remap the unexported path to the correct public export
'^some-package/lib/utils$': '<rootDir>/node_modules/some-package/dist/utils.js'
}
};
# Run Jest with experimental VM modules for ESM packages
node --experimental-vm-modules node_modules/.bin/jest
Conditional Exports – Mismatch Scenario
Modern packages use conditional exports
to serve different files for ESM and CJS consumers. If your environment doesn't match any
condition and no "default" is defined, Node.js throws
ERR_PACKAGE_PATH_NOT_EXPORTED:
// Misconfigured – missing "default" causes error if environment doesn't match
{
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
// no "default" → ERR_PACKAGE_PATH_NOT_EXPORTED in unexpected runtimes
}
}
}
// Correct – always include "default" as the last condition
{
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js",
"default": "./dist/index.js"
}
}
}
Intentionally Private Paths (null exports)
The exports field supports null values to explicitly block access to
certain subpaths. This is an intentional design choice — the package author is signaling that
those paths are private implementation details:
{
"exports": {
"./features/*.js": "./src/features/*.js",
"./features/private-internal/*": null // explicitly blocked
}
}
// This throws ERR_PACKAGE_PATH_NOT_EXPORTED — blocked by null
import m from 'es-module-package/features/private-internal/m.js';
If a package intentionally blocks a path with null, you cannot work around it
without patching the package. You must use the public API.
TypeScript and moduleResolution
TypeScript's legacy "moduleResolution": "node" does not understand the
exports field. TypeScript may compile successfully while the emitted JavaScript
fails at runtime with ERR_PACKAGE_PATH_NOT_EXPORTED. Update
tsconfig.json to use a modern resolution mode:
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "bundler", // best for Vite, esbuild, webpack
// or:
"moduleResolution": "node16", // for native Node.js ESM with package.json type:module
// or:
"moduleResolution": "nodenext" // strict Node.js ESM resolution
}
}
Affected Node.js Versions
| Node.js version | Behaviour |
|---|---|
| < 12.7 | exports field ignored; deep imports always work |
| 12.7 – 12.16 | exports honoured but with warnings only |
| 12.17+ / 14+ | exports strictly enforced; error thrown on violation |
| 16, 18, 20, 22 (LTS) | Strict enforcement; no opt-out flag available |
Debugging Checklist
- Read the error message — it tells you the exact package name and subpath that is missing.
- Open
node_modules/<package>/package.jsonand inspect theexportsfield to see what paths are actually available. - Check the package's GitHub releases or
CHANGELOG.mdfor migration notes — the new import path is usually documented there. - Search the package's issue tracker for
ERR_PACKAGE_PATH_NOT_EXPORTED— you are almost certainly not alone. - Run
node --versionto confirm you are on a version that enforcesexports(12.17+). - Run
npm list <package-name>to check if multiple conflicting versions of the package are installed. - If the error appeared after
npm installwith no code changes, do a clean reinstall — a stale lockfile can install mixed versions. - If you are in a TypeScript project, verify your
"moduleResolution"setting intsconfig.jsonis"node16","nodenext", or"bundler".
Real-World Package Examples
| Package | Broken import | Correct import |
|---|---|---|
uuid (v8+) |
import uuidv4 from 'uuid/v4' |
import { v4 as uuidv4 } from 'uuid' |
axios |
import Axios from 'axios/lib/core/Axios' |
import axios from 'axios' |
firebase-admin |
require('firebase-admin/lib/...') |
Use named exports: import { getAuth } from 'firebase-admin/auth' |
langchain |
import { ... } from 'langchain/openai' |
import { ... } from '@langchain/openai' (split package) |
eslint |
require('eslint/lib/...') |
Use the ESLint public API or require('eslint') |
jest-resolve |
require('jest-resolve/build/defaultResolver') |
Upgrade Jest to v28+; use the public resolver API |
Frequently Asked Questions
What is ERR_PACKAGE_PATH_NOT_EXPORTED?
ERR_PACKAGE_PATH_NOT_EXPORTED is a Node.js runtime error thrown when code tries to import or require() a subpath inside an npm package that is not listed in that package's "exports" field in package.json. Node.js enforces the exports map strictly — any path not explicitly listed is private and inaccessible, even if the file physically exists on disk.
Why does this error appear with uuid?
The uuid package removed subpath imports in version 8. The old pattern import uuidv4 from 'uuid/v4' no longer works because 'uuid/v4' is not in the package's exports map. Replace it with named exports from the root:
import { v4 as uuidv4 } from 'uuid'; (ESM)
const { v4: uuidv4 } = require('uuid'); (CommonJS)
Why does this error appear with Jest?
Jest versions before 28 do not honour the exports field. When Jest's resolver tries to load a path that a package only exposes through its exports map, it may fall back to a direct file-system path that isn't exported, causing ERR_PACKAGE_PATH_NOT_EXPORTED. Fix: upgrade Jest to v28+, which has native exports support. For older Jest versions, add a moduleNameMapper in jest.config.js to redirect the import.
What does it mean when an exports entry is null?
Setting an exports entry to null (e.g. "./internal/*": null) explicitly marks that subpath as intentionally private. Attempting to import it throws ERR_PACKAGE_PATH_NOT_EXPORTED. This is a deliberate design choice by the package author. You must use the package's public API — the only workaround is to patch the dependency's package.json with patch-package.
How do I expose a subpath in my own package's exports field?
Add the subpath to the "exports" field in your package.json. Use specific paths for individual files, or wildcard patterns for entire directories:
{
"exports": {
".": "./dist/index.js",
"./utils": "./dist/utils.js",
"./models/*": "./dist/models/*.js",
"./package.json": "./package.json"
}
}
How do I fix this in a TypeScript project?
TypeScript's legacy "moduleResolution": "node" ignores the exports field, so TypeScript compiles without error while the emitted JavaScript fails at runtime. Update your tsconfig.json:
For Vite, webpack, or esbuild projects: "moduleResolution": "bundler"
For native Node.js ESM: "moduleResolution": "node16" or "nodenext"
Does a clean reinstall fix ERR_PACKAGE_PATH_NOT_EXPORTED?
Sometimes. A corrupted or mixed-version node_modules tree can expose an older package.json with a different exports map than the installed version. A clean reinstall resolves this: delete node_modules and package-lock.json, run npm cache clean --force, then npm install. If the error persists after a clean reinstall, the cause is in your import paths or the package configuration itself.
Related Errors
Error: Cannot find module (MODULE_NOT_FOUND)— the file doesn't exist at all (vs. existing but not exported)ERR_REQUIRE_ESM— trying torequire()a pure ES module; often appears together withERR_PACKAGE_PATH_NOT_EXPORTEDwhen migrating packagesSyntaxError: Cannot use import statement in a module— ESM/CJS interop errors that appear in the same module system migration contextERR_PACKAGE_IMPORT_NOT_DEFINED— the"imports"map (self-referencing) equivalent of this error; occurs with#internal-style package imports