Skip to content

module: add requireStack to all error paths#62059

Open
Han5991 wants to merge 3 commits into
nodejs:mainfrom
Han5991:fix/cjs-loader-require-stack
Open

module: add requireStack to all error paths#62059
Han5991 wants to merge 3 commits into
nodejs:mainfrom
Han5991:fix/cjs-loader-require-stack

Conversation

@Han5991
Copy link
Copy Markdown
Contributor

@Han5991 Han5991 commented Mar 1, 2026

The CJS loader attaches requireStack only on the bottom fallback
path of Module._resolveFilename. Errors from tryPackage,
trySelf, the #imports branch, finalizeEsmResolution, and from
within Module._findPath (exports/imports resolution failures)
escape without it. This PR makes the diagnostic uniform across
those paths.

Resolves the two TODO(BridgeAR) comments deferred in #26823
(2019) and carried through #34744 (2020).

The second commit on this branch addresses self-review findings:
broader module-resolution error-code filter, lazy parent-chain walk
(no hot-path cost), and an ObjectIsExtensible guard so the
attachment cannot mask the original error from a monkey-patched
Module._findPath. Commit messages contain the full design notes.

Tests in test/parallel/test-module-require-stack-paths.js cover
the newly attached paths; existing tests are extended where the
property was previously absent.

@nodejs-github-bot
Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/loaders

@nodejs-github-bot nodejs-github-bot added module Issues and PRs related to the module subsystem. needs-ci PRs that need a full CI run. labels Mar 1, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 1, 2026

Codecov Report

❌ Patch coverage is 98.59155% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 90.15%. Comparing base (8d3245e) to head (34c1289).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/modules/cjs/loader.js 98.59% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #62059   +/-   ##
=======================================
  Coverage   90.14%   90.15%           
=======================================
  Files         718      718           
  Lines      227984   228042   +58     
  Branches    42835    42841    +6     
=======================================
+ Hits       205522   205589   +67     
+ Misses      14235    14231    -4     
+ Partials     8227     8222    -5     
Files with missing lines Coverage Δ
lib/internal/modules/cjs/loader.js 98.14% <98.59%> (+0.09%) ⬆️

... and 27 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Han5991 Han5991 force-pushed the fix/cjs-loader-require-stack branch 2 times, most recently from 17b4a9e to 81f253b Compare May 23, 2026 14:04
Previously, `requireStack` was attached only on the bottom fallback
path of `Module._resolveFilename`. Errors thrown from `tryPackage`,
`trySelf`, the `#imports` branch, `finalizeEsmResolution`, and from
within `Module._findPath` (exports/imports resolution failures) all
escaped without it, leaving the diagnostic uneven depending on
which inner path failed.

This change makes the diagnostic uniform across those paths via
two helpers:

* `buildRequireStack(parent)` walks the parent chain. It is called
  lazily on the error path only, so successful resolves keep their
  original cost and a pathological parent-chain cycle from
  `module.parent = module` cannot hang the hot path.
* `attachRequireStack(err, parent)` applies the stack to an error
  when (a) the error code is in a `SafeSet` of module-resolution
  codes (`MODULE_NOT_FOUND`, `ERR_MODULE_NOT_FOUND`,
  `ERR_PACKAGE_PATH_NOT_EXPORTED`, `ERR_PACKAGE_IMPORT_NOT_DEFINED`,
  `ERR_INVALID_MODULE_SPECIFIER`, `ERR_INVALID_PACKAGE_TARGET`,
  `ERR_INVALID_PACKAGE_CONFIG`), (b) `err.requireStack` is not
  already an array, and (c) the error object is extensible. The
  last check protects against monkey-patched `Module._findPath`
  implementations that may throw frozen errors.

The catch blocks in the `#imports`, `trySelf`, and
`Module._findPath` paths delegate to `attachRequireStack`, and the
bottom fallback uses `buildRequireStack` directly.

Resolves two long-standing `TODO(BridgeAR): Add the requireStack
as well` comments in `lib/internal/modules/cjs/loader.js`,
originally deferred in nodejs#26823 (2019) and carried through nodejs#34744
(2020).

Tests cover the `#imports`-to-missing-file case, the
`ERR_PACKAGE_PATH_NOT_EXPORTED` case via self-require, a frozen-
error monkey-patch (must not crash), and a preset non-array
`requireStack` value (must be replaced). Existing assertions in
`test-module-loading.js` and `test-module-loading-error.js` are
extended to verify the new property on errors that previously
lacked it.

Signed-off-by: sangwook <rewq5991@gmail.com>
@Han5991 Han5991 force-pushed the fix/cjs-loader-require-stack branch from 81f253b to 9fa4631 Compare May 23, 2026 14:33
Han5991 added 2 commits May 23, 2026 23:39
Signed-off-by: sangwook <rewq5991@gmail.com>
Signed-off-by: sangwook <rewq5991@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module Issues and PRs related to the module subsystem. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants