GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — fs: report errors from recursive watch events by codeNinja62 · Pull Request #66792 · nodejs/node

Conversation

@codeNinja62

@codeNinja62 codeNinja62 commented Oct 16, 2026

Copy link
Copy Markdown

Fixes: #66791

On Linux, recursive fs.watch() is the JavaScript watcher in lib/internal/fs/recursive_watch.js. Since #65486 it handles each inotify event with a stat()/lstat() of the entry it names, and calls fs.watch() on directories and symlinks that show up later. When one of those calls fails with anything other than ENOENT, the error is thrown inside the FSEvent callback, where no 'error' listener can see it, and the process exits:

Trigger Thrown from v24.22.0, v26.9.0, v26.12.0 v24.21.0, v26.4.0, v26.8.2
watched directory becomes unreadable (chmod 000) lstatSync() in #onFolderEvent() uncaught EACCES, exit 1 'error', keeps running
new directory already unreadable when its event is handled fs.watch() in #watch() uncaught EACCES, exit 1 'error', keeps running
new symlink into an unreadable directory, or a symlink loop fs.watch() in #watch() uncaught EACCES / ELOOP, exit 1 'error', keeps running
watched directory replaced by a file while events for its entries are queued lstatSync() in #onFolderEvent() uncaught ENOTDIR, exit 1 keeps running, no event

What this changes:

  • The statSync()/lstatSync() calls in #onFolderEvent() and #onEntryEvent() now report failures through 'error', the same way #scanFolder() already handles readdirSync() failures (fs: ignore deleted dirs in recursive watch scan #63686). ENOTDIR from lstatSync() is treated as the entry being gone, which is what statSync() with throwIfNoEntry: false and #scanFolder() already do.
  • #watch() now gets the initial flag its callers already have. After the initial scan, a failure to watch a directory or symlink is reported through 'error' and the entry is dropped, so a later event about it (for example the chmod that makes it readable again) picks it up. During the initial scan, errors still propagate out of fs.watch(), as fs: fix recursive watch error handling #65635 and fs: handle recursive watch setup races #65699 intended.

Why 'error' instead of silently forgetting the entry: the watcher can no longer see inside that directory, the native watchers report this kind of failure through 'error' too, and long-running consumers such as file-watching daemons need to know. Without an 'error' listener the emitter still throws, like any other FSWatcher error.

I didn't add a #closed check before the new emit('error') calls: both handlers return early once the watcher is closed, and so does #watch(). The only code that can run in between is a user-supplied ignore function, which can already lead to 'change' being emitted after close().

Tests (each one crashes the process without this change and passes with it):

  • test-fs-watch-recursive-unreadable-dir.js: a directory made unreadable while watched, and a directory that is already unreadable when its creation is handled and is picked up once it becomes readable. Skipped as root.
  • test-fs-watch-recursive-dir-replaced-by-file.js: the ENOTDIR case; no 'error' is expected.
  • test-fs-watch-recursive-symlink-loop.js: a symlink loop created while watching; 'error' with ELOOP is expected. This covers the symlink branch of #addEntry().

Checked locally on a build of main (Ubuntu on WSL2): the new tests fail before the change and pass after it, 10 runs in a row; tools/test.py 'parallel/test-fs-watch*' 'sequential/test-fs-watch*' passes (49 tests); make lint-js is clean.

v24.22.0 has the same code, since #65486 landed there too, so this should go to v24.x as well.

I used an AI coding assistant to help investigate this and draft the change. I reproduced the crashes myself, reviewed the diff, and ran the tests and lint locally.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@codeNinja62
fs: report errors from recursive watch events
5e61a8c
On Linux, the recursive fs.watch() fallback resolves inotify events
with stat() and lstat() calls, and arms fs.watch() on directories and
symbolic links that appear later. Errors from those calls other than
ENOENT were thrown from inside the FSEvent callback, where nothing can
catch them, so the process exited: EACCES when a watched directory
became unreadable, or when a new directory or the target of a new
symbolic link was unreadable by the time it was handled; ELOOP for a
symbolic link loop; and ENOTDIR when a watched directory was replaced
by a file while events naming its entries were still queued.

Report such errors through 'error', as #scanFolder() already does for
readdir(), and take ENOTDIR from lstat() to mean that the entry is
gone, as statSync() and #scanFolder() do. A directory or link that
cannot be watched is dropped, so a later event about it picks it up
again. Failures during the initial scan still propagate out of
fs.watch().

Fixes: nodejs#66791
Signed-off-by: Sameer Ahmed <[email protected]>
@nodejs-github-bot nodejs-github-bot added fs Issues and PRs related to file-system APIs and the fs module. needs-ci PRs that need a full CI run. labels Oct 16, 2026
@github-actions

github-actions Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Welcome to Node.js, and thank you for your first contribution!

Before review, please take a moment to read:

  • the guide for first-time contributors
  • the contribution and automation policies
  • the pull request guide
  • the AI use policy
  • the Code of Conduct

Please make sure every commit is signed off. For a first pull request, GitHub Actions require collaborator approval and Jenkins CI must be started by a collaborator or triager, so an initial wait is normal.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Oct 27, 2026
fs.watch recursive crashes the process on Linux (uncaught EACCES) when a watched directory becomes unreadable #66791
Open
Docker image (Node 26.12.1) exits when a directory inside a dataDir is deleted: uncaught EBADF from Node's recursive fs.watch cross-seed/cross-seed#1258
Open
@github-actions github-actions Bot mentioned this pull request Nov 2, 2026
Update ghcr.io/cross-seed/cross-seed Docker tag to v6.13.9 claytono/infra#3320
Merged
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

fs Issues and PRs related to file-system APIs and the fs module. needs-ci PRs that need a full CI run.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

fs.watch recursive crashes the process on Linux (uncaught EACCES) when a watched directory becomes unreadable

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@codeNinja62 @nodejs-github-bot
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.