You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ESM resolver treats an unreadable package.json as absent: exports silently vanish and the wrong file can load #65220
v26.5.1 (behavior present from v22.x through main; see the source reference below)
Platform
Reproduced on darwin arm64; the code path is platform-independent.
Subsystem
module, esm
What steps will reproduce the bug?
mkdir -p repro/node_modules/dep/lib &&cd repro
printf'{"name":"dep","exports":{".":"./lib/real.js","./sub":"./lib/sub.js"}}'> node_modules/dep/package.json
printf'export const which = "real";'> node_modules/dep/lib/real.js
printf'export const which = "sub";'> node_modules/dep/lib/sub.js
printf'export const which = "decoy index.js";'> node_modules/dep/index.js
printf'{"type":"module"}'> package.json
cat > main.mjs <<'EOF'const sub = await import("dep/sub").then(m => m.which, e => `${e.code}`);const root = await import("dep").then(m => m.which, e => `${e.code}`);console.log("dep/sub ->", sub);console.log("dep ->", root);EOF
node main.mjs
# dep/sub -> sub# dep -> real
chmod 000 node_modules/dep/package.json # any read failure; EACCES stands in for EMFILE/EIO/ENOMEM
node main.mjs
# dep/sub -> ERR_MODULE_NOT_FOUND# dep -> decoy index.js
What is the expected behavior?
A package.json that exists but cannot be read should surface the read error (or at minimum fail resolution with an error that names the errno), the way an unparsable package.json raises ERR_INVALID_PACKAGE_CONFIG.
What do you see instead?
The read failure is treated identically to the file being absent:
import("dep/sub") fails ERR_MODULE_NOT_FOUND for a subpath the package exports — with a Did you mean to import "dep/lib/sub.js"?-style hint in real packages, pointing users away from the actual problem.
import("dep")silently executes a different file than the package declares — legacy index.js resolution instead of the exports target. No error, no warning; wrong code runs.
The swallow is in src/node_modules.cc (GetPackageJSON):
if (ReadFileSync(&package_config.raw_json, path.data()) < 0) {
returnnullptr;
}
ReadFileSync's failure reason is discarded, so ENOENT (genuinely absent — the reasonable case to treat as "no package.json") is indistinguishable from EACCES, EMFILE, EIO, or ENOMEM on a file that exists.
Additional information
We hit this in CI as an unexplainable one-off: a correctly installed package with a correct exports map failed a subpath import with ERR_MODULE_NOT_FOUND on a heavily loaded runner, and everything on disk was byte-identical before and after. The root cause took a while to establish precisely because the errno is discarded — a transient read failure leaves no trace and masquerades as a missing module (taras/executable.md#439 has the investigation). Distinguishing ENOENT from other errnos at this one spot would turn silent misresolution into a diagnosable error.
Related but distinct: #54304 (over-long path makes the read fail, module type misdetected — same swallow surfacing through a different trigger), #49674 (misleading message when package.json exists but defines no matching export).
Opened #65223 for this. GetPackageJSON() returned no config for any read failure, so an unreadable package.json was indistinguishable from a missing one. ENOENT, ENOTDIR and EISDIR still mean "no package config here" (the last one covers a package.json that is itself a directory, which test-module-loading-error relies on); anything else now reports the underlying error.
Activity
module: report unreadable package.json
bitpshr commentedon Aug 11, 2026
Opened #65223 for this.
GetPackageJSON()returned no config for any read failure, so an unreadablepackage.jsonwas indistinguishable from a missing one. ENOENT, ENOTDIR and EISDIR still mean "no package config here" (the last one covers apackage.jsonthat is itself a directory, whichtest-module-loading-errorrelies on); anything else now reports the underlying error.taras commentedon Aug 11, 2026
Awesome! thank you!
module: report unreadable package.json
module: report unreadable package.json
module: report unreadable package.json
module: report unreadable package.json
module: report unreadable package.json
module: report unreadable package.json
module: report unreadable package.json