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
git clone https://github.com/kabo/node-24.15-require-extensions-repro
cd node-24.15-require-extensions-repro/variant-b
touch yarn.lock
corepack yarn install
corepack yarn node run.mjs # crashes on v24.15.0; succeeds on v24.14.1
The reproducer is ~10 lines of code split across 4 files: a Yarn 4.14.1 PnP project (package.json), an ESM entry (run.mjs) that does await import('dummy-cjs'), and a trivial local CJS dependency (dummy-cjs/index.cjs) whose only relevant line is:
constoldJSHook=require.extensions['.js']// line that throws on v24.15.0
See the repo's README.md for full context, and output-variant-b-24.14.0.txt / output-variant-b-24.15.0.txt for the side-by-side outputs that establish the regression.
How often does it reproduce? Is there a required condition?
Every time on v24.15.0 when a CJS file is loaded via the ESM loader through a custom resolver/loader that produces a non-file URL (Yarn PnP loads from file://.../cache/....zip/node_modules/...).
What is the expected behavior? Why is that the expected behavior?
require.extensions should be defined on the require function passed into the CJS module wrapper. It is deprecated (DEP0007) but has been part of the public require surface since 0.12. Multiple libraries still in active use (Next.js next-config-ts/require-hook.js, ts-node, esbuild-register, pirates, etc.) read it at CJS module top level.
Confirmed behavior on v24.14.1:
node: v24.14.1
typeof require.extensions: object
.js hook: function
What do you see instead?
On v24.15.0:
[dummy-cjs] node: v24.15.0
[dummy-cjs] typeof require: function
[dummy-cjs] typeof require.extensions: undefined
file:///.../.yarn/berry/cache/dummy-cjs-file-6872e80d06-10.zip/node_modules/dummy-cjs/index.cjs:6
const oldJSHook = require.extensions['.js']
^
TypeError: Cannot read properties of undefined (reading '.js')
at Object.<anonymous> (file:///.../.yarn/berry/cache/dummy-cjs-file-6872e80d06-10.zip/node_modules/dummy-cjs/index.cjs:6:37)
at loadCJSModule (node:internal/modules/esm/translators:185:3)
at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:231:7)
at ModuleJob.run (node:internal/modules/esm/module_job:437:25)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async node:internal/modules/esm/loader:639:26
at async file:///.../run.mjs:1:1
Node.js v24.15.0
Note typeof require is function — a require is provided — but require.extensions is undefined on it, which differs from what CJS modules (and the Module.prototype.require used in filesystem-loaded CJS) see.
Additional information
Suspected cause: nodejs/node#61769 ("lib: reduce cycles in esm loader and load it in snapshot"), which rewrote the ESM loader initialization and the synthesised require construction for CJS-from-ESM. That PR landed in v24.x for the first time in v24.15.0.
Related bug in same subsystem: nodejs/node#62012 — EBADF fstat on zip fds in the same createCJSModuleWrap path. Same Node version, same Yarn PnP setup, different symptom in the same synthetic-CJS-wrap code.
Real-world impact: next build on Next.js 15+ fails for all users on Yarn PnP + Node 24.15 because Next's next/dist/build/next-config-ts/require-hook.js reads require.extensions['.js'] at module top level.
Not reproducible when the CJS file is loaded from a filesystem path (without a custom PnP-like loader in front), which suggests the bug is specific to how the ESM→CJS wrap constructs require for modules whose source URL is not a plain file:///.../something.cjs.
Version
v24.15.0 (fails). Works on v24.14.1 and earlier. Platform: Linux x64 (also reproduced inside
node:24.15.0-slimDocker image).Platform
Subsystem
loaders,module, specifically the CJS-from-ESM translator (node:internal/modules/esm/translators).What steps will reproduce the bug?
Reproducer repo: https://github.com/kabo/node-24.15-require-extensions-repro
The reproducer is ~10 lines of code split across 4 files: a Yarn 4.14.1 PnP project (
package.json), an ESM entry (run.mjs) that doesawait import('dummy-cjs'), and a trivial local CJS dependency (dummy-cjs/index.cjs) whose only relevant line is:See the repo's
README.mdfor full context, andoutput-variant-b-24.14.0.txt/output-variant-b-24.15.0.txtfor the side-by-side outputs that establish the regression.How often does it reproduce? Is there a required condition?
Every time on v24.15.0 when a CJS file is loaded via the ESM loader through a custom resolver/loader that produces a non-file URL (Yarn PnP loads from
file://.../cache/....zip/node_modules/...).What is the expected behavior? Why is that the expected behavior?
require.extensionsshould be defined on therequirefunction passed into the CJS module wrapper. It is deprecated (DEP0007) but has been part of the publicrequiresurface since 0.12. Multiple libraries still in active use (Next.jsnext-config-ts/require-hook.js, ts-node, esbuild-register, pirates, etc.) read it at CJS module top level.Confirmed behavior on v24.14.1:
What do you see instead?
On v24.15.0:
Note
typeof requireisfunction— arequireis provided — butrequire.extensionsisundefinedon it, which differs from what CJS modules (and theModule.prototype.requireused in filesystem-loaded CJS) see.Additional information
requireconstruction for CJS-from-ESM. That PR landed in v24.x for the first time in v24.15.0.createCJSModuleWrappath. Same Node version, same Yarn PnP setup, different symptom in the same synthetic-CJS-wrap code.next buildon Next.js 15+ fails for all users on Yarn PnP + Node 24.15 because Next'snext/dist/build/next-config-ts/require-hook.jsreadsrequire.extensions['.js']at module top level.requirefor modules whose source URL is not a plainfile:///.../something.cjs.Workaround
require.extensions: use optional chaining (require.extensions?.['.js']) so the call at module load doesn't throw.