The 60-Second Fix
Ever tried importing a specific utility from a dependency only to have Node.js slam the door in your face? This error happens because the package author has used the "exports" field to lock down their internal files. If a file isn't on the "public" list, Node.js simply won't let you touch it.
Try these steps to get moving again:
- Check the docs: You might be trying to grab an internal file that wasn't meant for public use. Look for the official way to access that feature.
- Shorten your import: Instead of
import { helper } from 'my-lib/dist/utils', try the cleanerimport { helper } from 'my-lib/utils'or justimport { helper } from 'my-lib'. - Check for updates: Maintainers often hide subpaths when they switch to ESM (ECMAScript Modules). If you just updated the package, check the version history for breaking changes.
Why is Node.js Blocking Your Import?
Before Node.js 12.17.0, you could reach into any npm package and grab whatever you wanted. It was like walking into a restaurant and wandering straight into the kitchen to grab a fork. While convenient, it meant maintainers couldn't change their folder structure without breaking thousands of apps.
Node.js fixed this with Package Encapsulation. By adding an "exports" block to package.json, authors can now define exactly which files are part of the public API. It acts like a gatekeeper. If it’s not on the list, it doesn’t exist to the outside world.
// Inside node_modules/some-package/package.json
{
"name": "some-package",
"exports": {
".": "./index.js",
"./feature": "./src/feature.js"
}
}
In this scenario, trying to import some-package/src/feature.js directly will fail with the ERR_PACKAGE_PATH_NOT_EXPORTED error. You have to use the alias some-package/feature instead.
Three Ways to Solve It
1. Use the Defined Entry Points
Open the package.json file of the library inside your node_modules folder. Scroll down to the "exports" section. These keys are the only valid ways to import the code.
If the package defines:
"exports": {
".": "./dist/main.js",
"./utils": "./dist/utils.js"
}
Stop trying to point directly to the dist folder. Change your code from this:
import { formatDate } from 'some-package/dist/utils.js'; // This will crash
To this cleaner version:
import { formatDate } from 'some-package/utils'; // This works perfectly
2. Exposing Subpaths (For Package Authors)
If you own the package that's causing the headache, you need to whitelist the missing paths. You don't have to list every file individually. Use wildcards to expose entire directories at once:
{
"exports": {
".": "./index.js",
"./lib/*": "./lib/*.js"
}
}
This allows your users to import any JS file inside the lib folder. It’s a great balance between security and flexibility.
3. The "Emergency" Patch
Sometimes you’re in a pinch and can't wait for a library author to merge a pull request. In those cases, use patch-package to fix it locally.
- Manually edit the
package.jsoninsidenode_modules/the-package/. - Add the missing path to the
"exports"object. - Run
npx patch-package the-package.
This creates a permanent fix in a patches/ folder that stays with your project even after you reinstall your dependencies.
How to Test Your Fix
A quick terminal command can save you a lot of debugging time. Run this one-liner to see if Node can resolve the path without running your whole app:
node --input-type=module -e "import('some-package/utils').then(() => console.log('Resolved!')).catch(console.error)"
If you see "Resolved!" instead of a stack trace, your import path is officially valid.
Avoid This Headache Next Time
Configuration errors are easy to make. A single missing comma in your package.json can make Node ignore your entire exports block. I usually run my config through a JSON Validator before publishing to catch those invisible syntax bugs. It’s much faster than debugging a failed CI build later.
Also, keep in mind that ESM is picky about file extensions. If your exports points to a file without the .js or .mjs extension, Node might throw a different but equally annoying error. Always be explicit with your paths to keep the module loader happy.

