Skip to content

watch: strip watch flags from NODE_OPTIONS in child process#62143

Open
marcopiraccini wants to merge 4 commits intonodejs:mainfrom
marcopiraccini:fix/watch-node-options-infinite-loop
Open

watch: strip watch flags from NODE_OPTIONS in child process#62143
marcopiraccini wants to merge 4 commits intonodejs:mainfrom
marcopiraccini:fix/watch-node-options-infinite-loop

Conversation

@marcopiraccini
Copy link
Contributor

@marcopiraccini marcopiraccini commented Mar 7, 2026

When NODE_OPTIONS contains --watch, the watch mode parent process spawns a child that inherits the same NODE_OPTIONS, causing the child to also enter watch mode and spawn another child, creating an infinite loop of process spawning.

This PR strips watch-related flags (--watch, --watch-path,--watch-preserve-output, --watch-kill-signal) from NODE_OPTIONS before passing the environment to the child process. This mirrors the existing logic that already strips these flags from process.execArgv.

Non-watch flags in NODE_OPTIONS (e.g., --max-old-space-size) are preserved.

To properly handle quoted strings and backslash escapes in NODE_OPTIONS values (e.g., --require "./path with --watch in name/f.js"), the C++ ParseNodeOptionsEnvVar tokenizer is exposed to JavaScript via a new internalBinding('options') method. This reuses the exact same parser that Node.js uses at startup, ensuring consistent behavior.

Fixes: #61740

This is actually similar to #61838 but more "surgical", it just avoid propagating watch flags.

Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
@nodejs-github-bot nodejs-github-bot added the needs-ci PRs that need a full CI run. label Mar 7, 2026
@marcopiraccini marcopiraccini marked this pull request as ready for review March 7, 2026 05:56
@codecov
Copy link

codecov bot commented Mar 7, 2026

Codecov Report

❌ Patch coverage is 90.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.65%. Comparing base (3725bd2) to head (91e21a9).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
src/node_options.cc 88.88% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #62143   +/-   ##
=======================================
  Coverage   89.65%   89.65%           
=======================================
  Files         676      676           
  Lines      206543   206563   +20     
  Branches    39547    39558   +11     
=======================================
+ Hits       185184   185202   +18     
+ Misses      13480    13471    -9     
- Partials     7879     7890   +11     
Files with missing lines Coverage Δ
lib/internal/options.js 47.19% <100.00%> (+0.60%) ⬆️
src/node_options.cc 77.15% <88.88%> (+0.70%) ⬆️

... and 32 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.

let cleanNodeOptions = kNodeOptions;
if (kNodeOptions != null) {
const nodeOptionsArgs = [];
const parts = RegExpPrototypeSymbolSplit(/\s+/, kNodeOptions);
Copy link
Member

@Renegade334 Renegade334 Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultimately, this sort of approach isn't robust enough.

$ mkdir 'test for --watch parsing'
$ echo 'console.log("hi!")' > 'test for --watch parsing/test.cjs'
$ NODE_OPTIONS='--require "./test for --watch parsing/test.cjs"' node -p 'process.env.NODE_OPTIONS.split(" ")'
hi!
[ '--require', '"./test', 'for', '--watch', 'parsing/test.cjs"' ]
                                  ^^^^^^^

I suspect this is going to need to interact with the options parser in some way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.
Thinking about it, maybe adding a C++ binding that exposes ParseNodeOptionsEnvVar to JS would be more robust (being the same parser to interpret NODE_OPTIONS, in my undersanding) and then finltering out watch flags. Will amend this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Renegade334 Renegade334 added the watch-mode Issues and PRs related to watch mode label Mar 8, 2026
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ci PRs that need a full CI run. watch-mode Issues and PRs related to watch mode

Projects

None yet

Development

Successfully merging this pull request may close these issues.

watch: NODE_OPTIONS=--watch causes infinite loop

3 participants