Skip to content

watch: fix --env-file-if-exists crashing on linux if the file is missing#61870

Open
efekrskl wants to merge 7 commits intonodejs:mainfrom
efekrskl:fix/watch-mode-optional-env-crash
Open

watch: fix --env-file-if-exists crashing on linux if the file is missing#61870
efekrskl wants to merge 7 commits intonodejs:mainfrom
efekrskl:fix/watch-mode-optional-env-crash

Conversation

@efekrskl
Copy link
Contributor

Fixes #57040

--env-file-if-exists combined with --watch seems to crash in if the .env file is missing (in Linux), this PR aims to fix that.

@nodejs-github-bot nodejs-github-bot added the needs-ci PRs that need a full CI run. label Feb 17, 2026
@codecov
Copy link

codecov bot commented Feb 17, 2026

Codecov Report

❌ Patch coverage is 96.29630% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 89.76%. Comparing base (4f13746) to head (232e0a3).
⚠️ Report is 22 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/fs/recursive_watch.js 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #61870      +/-   ##
==========================================
+ Coverage   89.72%   89.76%   +0.04%     
==========================================
  Files         675      674       -1     
  Lines      204806   204903      +97     
  Branches    39355    39386      +31     
==========================================
+ Hits       183761   183940     +179     
+ Misses      13330    13234      -96     
- Partials     7715     7729      +14     
Files with missing lines Coverage Δ
lib/fs.js 98.19% <100.00%> (+<0.01%) ⬆️
lib/internal/fs/watchers.js 88.03% <100.00%> (+0.73%) ⬆️
lib/internal/watch_mode/files_watcher.js 90.69% <100.00%> (+0.08%) ⬆️
lib/internal/fs/recursive_watch.js 85.27% <88.88%> (+0.36%) ⬆️

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

@Renegade334 Renegade334 added the watch-mode Issues and PRs related to watch mode label Feb 18, 2026
Comment on lines 110 to 111
if (existsSync(resolvedPath)) {
watcher.filterFile(resolvedPath);
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a TOCTOU race condition here, the file could be deleted between the existSync call and the watcher.filterFile. Could we use something reliable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, good point. I've changed the implementation a bit, instead of using existsSync, now we would try/catch and swallow the errors. This seems to be the approach in some other places. WDYT?

@efekrskl efekrskl requested a review from aduh95 February 18, 2026 19:06
Comment on lines 108 to 115
try {
watcher.filterFile(resolve(file));
} catch (error) {
if (error?.code !== 'ENOENT' && error?.code !== 'ENOTDIR') {
throw error;
}
// Failed watching the file, ignore
}
Copy link
Member

Choose a reason for hiding this comment

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

This is a really unperformant way of doing this. Creating, throwing and catching errors is extremely costly. Ideally just like fs.statSync we should have a way of not throwing ENOENT errors from this method.


if (throwIfNoEntry != null) {
validateBoolean(throwIfNoEntry, 'options.throwIfNoEntry');
}
Copy link
Member

@anonrig anonrig Feb 19, 2026

Choose a reason for hiding this comment

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

Can you just add a default value, which will simplify tracing?

Suggested change
}
} else {
throwIfNoEntry = true;
}

}
} catch (error) {
if (error.code === 'ENOENT') {
if (this.#options.throwIfNoEntry !== false && error.code === 'ENOENT') {
Copy link
Member

Choose a reason for hiding this comment

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

If you add a default value, this will be less error prune.

Suggested change
if (this.#options.throwIfNoEntry !== false && error.code === 'ENOENT') {
if (!this.#options.throwIfNoEntry && error.code === 'ENOENT') {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added the default, but I think using ! instead of explicitly checking for false would be against our intention, no?

ArrayPrototypeForEach(kEnvFiles, (file) => watcher.filterFile(resolve(file)));
}
if (kOptionalEnvFiles.length > 0) {
ArrayPrototypeForEach(kOptionalEnvFiles, (file) => watcher.filterFile(resolve(file), undefined, true));
Copy link
Member

Choose a reason for hiding this comment

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

Since you didn't use options like an object, it is now extremely hard to trace this method. If you prefer to use boolean value for passing the throwIfNoEntry, which might be something you prefer (or not), I recommend adding a small comment next to the parameter to make things a lot more maintainable (and readable) for others.

Suggested change
ArrayPrototypeForEach(kOptionalEnvFiles, (file) => watcher.filterFile(resolve(file), undefined, true));
ArrayPrototypeForEach(kOptionalEnvFiles, (file) => watcher.filterFile(resolve(file), undefined, true /* throwIfNoEntry */));

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, ok, thanks. In that case I think it'd make more sense to have an options object. Done

@efekrskl
Copy link
Contributor Author

@anonrig I've addressed all comments. I've also documented the change in fs.watch options and added some tests, I'm guessing this would be required as it is public facing.

Also, thanks a lot for the quick and detailed feedback!

@efekrskl efekrskl requested a review from anonrig February 19, 2026 22:33
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.

--env-file-if-exists throws error when the .env file doesn't exist and when combined with --watch (now in lts)

5 participants

Comments