Skip to content

Commit 7047f05

Browse files
committed
fs: fix recursive watch error handling
Recursive watch inverted the throwIfNoEntry check during setup, causing the default behavior to suppress ENOENT while the opt-out threw it. Other setup errors were also silently ignored. Suppress only ENOENT when throwIfNoEntry is false. Propagate all other setup failures and close watchers created before a partial failure. Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex
1 parent c3035a4 commit 7047f05

2 files changed

Lines changed: 59 additions & 1 deletion

File tree

lib/internal/fs/recursive_watch.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,9 @@ class FSWatcher extends EventEmitter {
234234
this.#watchFolder(filename);
235235
}
236236
} catch (error) {
237-
if (!this.#options.throwIfNoEntry && error.code === 'ENOENT') {
237+
if (this.#options.throwIfNoEntry || error.code !== 'ENOENT') {
238238
error.filename = filename;
239+
this.close();
239240
throw error;
240241
}
241242
}

test/parallel/test-fs-watch-enoent.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const {
1717
UV_ENODEV,
1818
UV_ENOENT
1919
} = internalBinding('uv');
20+
const { FSWatcher: RecursiveFSWatcher } = require('internal/fs/recursive_watch');
21+
const { kFSWatchStart } = require('internal/fs/watchers');
2022

2123
tmpdir.refresh();
2224

@@ -66,6 +68,61 @@ tmpdir.refresh();
6668
}
6769
}
6870

71+
{
72+
assert.throws(
73+
() => fs.watch(nonexistentFile, {
74+
recursive: true,
75+
throwIfNoEntry: true,
76+
}, common.mustNotCall()),
77+
{
78+
path: nonexistentFile,
79+
filename: nonexistentFile,
80+
code: 'ENOENT',
81+
},
82+
);
83+
}
84+
85+
{
86+
const watcher = fs.watch(nonexistentFile, {
87+
recursive: true,
88+
throwIfNoEntry: false,
89+
}, common.mustNotCall());
90+
watcher.close();
91+
}
92+
93+
{
94+
const directory = tmpdir.resolve('recursive-watch-error');
95+
const expected = new Error('recursive watcher failed');
96+
const originalWatch = fs.watch;
97+
const watcher = new RecursiveFSWatcher({ recursive: true });
98+
const close = common.mustCall();
99+
let calls = 0;
100+
101+
expected.code = 'ENOSPC';
102+
fs.mkdirSync(directory);
103+
fs.writeFileSync(`${directory}/file`, '');
104+
fs.watch = common.mustCall(() => {
105+
if (calls++ === 0) {
106+
return { close };
107+
}
108+
throw expected;
109+
}, 2);
110+
111+
try {
112+
assert.throws(
113+
() => watcher[kFSWatchStart](directory),
114+
(error) => {
115+
assert.strictEqual(error, expected);
116+
assert.strictEqual(error.filename, directory);
117+
return true;
118+
},
119+
);
120+
} finally {
121+
fs.watch = originalWatch;
122+
watcher.close();
123+
}
124+
}
125+
69126
{
70127
if (common.isMacOS || common.isWindows) {
71128
const file = tmpdir.resolve('file-to-watch');

0 commit comments

Comments
 (0)