From 61bfdb338e65f2c47f59f535b7306c31e2151437 Mon Sep 17 00:00:00 2001 From: sangwook Date: Sat, 3 Jan 2026 18:06:19 +0900 Subject: [PATCH 1/2] fs: fix ENOTDIR in globSync when file is treated as dir `fs.globSync` failed with `ENOTDIR` when a path component in a glob pattern was a file but used as a directory (e.g., 'foo{,/bar}' when 'foo' is a file). This change aligns `getDirentSync` with the asynchronous `getDirent` by wrapping the `lstatSync` call in a `try-catch` block to safely return `null` on such errors. Fixes: https://github.com/nodejs/node/issues/61257 --- lib/internal/fs/glob.js | 10 +++++++--- test/parallel/test-fs-glob.mjs | 20 +++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/internal/fs/glob.js b/lib/internal/fs/glob.js index 1bfa39150e5196..5e009f185c1554 100644 --- a/lib/internal/fs/glob.js +++ b/lib/internal/fs/glob.js @@ -65,11 +65,15 @@ async function getDirent(path) { * @returns {DirentFromStats|null} */ function getDirentSync(path) { - const stat = lstatSync(path, { throwIfNoEntry: false }); - if (stat === undefined) { + try { + const stat = lstatSync(path, { throwIfNoEntry: false }); + if (stat === undefined) { + return null; + } + return new DirentFromStats(basename(path), stat, dirname(path)); + } catch { return null; } - return new DirentFromStats(basename(path), stat, dirname(path)); } /** diff --git a/test/parallel/test-fs-glob.mjs b/test/parallel/test-fs-glob.mjs index 54523219cc2d97..74791deba373e3 100644 --- a/test/parallel/test-fs-glob.mjs +++ b/test/parallel/test-fs-glob.mjs @@ -2,7 +2,7 @@ import * as common from '../common/index.mjs'; import tmpdir from '../common/tmpdir.js'; import { resolve, dirname, sep, relative, join, isAbsolute } from 'node:path'; import { mkdir, writeFile, symlink, glob as asyncGlob } from 'node:fs/promises'; -import { glob, globSync, Dirent, chmodSync } from 'node:fs'; +import { glob, globSync, Dirent, chmodSync, writeFileSync, rmSync } from 'node:fs'; import { test, describe } from 'node:test'; import { pathToFileURL } from 'node:url'; import { promisify } from 'node:util'; @@ -543,3 +543,21 @@ describe('glob - with restricted directory', function() { } }); }); + +describe('globSync - ENOTDIR', function() { + test('should return empty array when a file is treated as a directory', () => { + const file = tmpdir.resolve('foo'); + writeFileSync(file, ''); + try { + const pattern = 'foo{,/bar}'; + const actual = globSync(pattern, { cwd: tmpdir.path }).sort(); + assert.deepStrictEqual(actual, ['foo']); + } finally { + try { + rmSync(file); + } catch { + // ignore + } + } + }); +}); From d2e58e34df54931f02ff285e75d7db0572dd347c Mon Sep 17 00:00:00 2001 From: sangwook <73056306+Han5991@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:36:59 +0900 Subject: [PATCH 2/2] fs: simplify getDirentSync lstatSync handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: René --- lib/internal/fs/glob.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/internal/fs/glob.js b/lib/internal/fs/glob.js index 5e009f185c1554..526efd4c010d7c 100644 --- a/lib/internal/fs/glob.js +++ b/lib/internal/fs/glob.js @@ -65,15 +65,13 @@ async function getDirent(path) { * @returns {DirentFromStats|null} */ function getDirentSync(path) { + let stat; try { - const stat = lstatSync(path, { throwIfNoEntry: false }); - if (stat === undefined) { - return null; - } - return new DirentFromStats(basename(path), stat, dirname(path)); + stat = lstatSync(path); } catch { return null; } + return new DirentFromStats(basename(path), stat, dirname(path)); } /**