Node.js Error

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]

error code: ERR_PACKAGE_PATH_NOT_EXPORTED

Complete reference — what it means, every real-world cause, and how to fix it permanently.

Quick Answer: Node.js throws 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

Terminal output (ESM / import):
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/utils' is not defined by "exports" in /project/node_modules/some-package/package.json
Terminal output (CJS / require):
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/parser' is not defined by "exports" in /project/node_modules/some-package/package.json
uuid-specific variant:
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

SituationWhy it happensFix
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"
  }
}
Warning: Adding "./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 versionBehaviour
< 12.7exports field ignored; deep imports always work
12.7 – 12.16exports 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

  1. Read the error message — it tells you the exact package name and subpath that is missing.
  2. Open node_modules/<package>/package.json and inspect the exports field to see what paths are actually available.
  3. Check the package's GitHub releases or CHANGELOG.md for migration notes — the new import path is usually documented there.
  4. Search the package's issue tracker for ERR_PACKAGE_PATH_NOT_EXPORTED — you are almost certainly not alone.
  5. Run node --version to confirm you are on a version that enforces exports (12.17+).
  6. Run npm list <package-name> to check if multiple conflicting versions of the package are installed.
  7. If the error appeared after npm install with no code changes, do a clean reinstall — a stale lockfile can install mixed versions.
  8. If you are in a TypeScript project, verify your "moduleResolution" setting in tsconfig.json is "node16", "nodenext", or "bundler".

Real-World Package Examples

PackageBroken importCorrect 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