Skip to content

fs: fix recursive readdir with buffer encoding - #64954

Open
canblmz1 wants to merge 1 commit into
nodejs:mainfrom
canblmz1:fix/readdir-recursive-buffer-path
Open

fs: fix recursive readdir with buffer encoding#64954
canblmz1 wants to merge 1 commit into
nodejs:mainfrom
canblmz1:fix/readdir-recursive-buffer-path

Conversation

@canblmz1

@canblmz1 canblmz1 commented Aug 2, 2026

Copy link
Copy Markdown

Description

fs.readdir(), fs.readdirSync(), and fs.promises.readdir() throw
ERR_INVALID_ARG_TYPE (or crash the process outright in the callback
case) when called with both { recursive: true } and
{ encoding: 'buffer' }. The internal recursive walk builds paths with
path.join()/path.relative(), and checks whether an entry is a
directory via a CommonJS-module-resolution-specific native stat
binding (internalModuleStat) - none of these accept Buffer
arguments, which is what entry names become once encoding: 'buffer'
is set.

const fs = require('fs');
fs.readdirSync('.', { recursive: true, encoding: 'buffer' });
// Uncaught TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument
// must be of type string. Received an instance of Buffer

Fix

  • internal/fs/utils already had an unexported join() helper that
    correctly handles string/Buffer combinations; this exports it and
    adds two more helpers alongside it: relativeToBasePath() (avoids
    needing path.relative()'s Buffer support, since the full path is
    always built by repeatedly joining onto the base path) and
    isDirectoryPath().
  • isDirectoryPath() keeps using internalModuleStat for the string
    fast path (unchanged behavior/performance for the common case), but
    falls back to the general-purpose stat binding - the same one
    fs.statSync() uses - for Buffer paths. That binding handles
    Buffers correctly at the native layer, so this also avoids a lossy
    string round-trip for non-UTF8 file names, rather than just papering
    over the reported crash.
  • lib/fs.js (handleDirents/handleFilePaths) and
    lib/internal/fs/promises.js (readdirRecursive) both had their
    own copy of this bug and are updated to use the shared helpers.

Testing

Added test/parallel/test-fs-readdir-recursive-buffer.js, covering
readdirSync, readdirSync with withFileTypes, the readdir
callback form, and fs.promises.readdir, all with
{ recursive: true, encoding: 'buffer' }.

Ran the full test/parallel/test-fs-readdir*.js suite plus a broader
test/parallel/test-fs-*.js sweep (257 files) locally on Windows
(clang-cl build) - all pass except 7 pre-existing failures unrelated
to this change (EPERM on symlinkSync due to this environment not
running elevated/without Developer Mode, verified individually).

Fixes: #58892

`fs.readdir()`, `fs.readdirSync()`, and `fs.promises.readdir()` threw
ERR_INVALID_ARG_TYPE (or crashed the process outright in the callback
case) when called with both `{ recursive: true }` and
`{ encoding: 'buffer' }`, because the internal recursive walk used
`path.join()`, `path.relative()`, and a CommonJS-module-resolution
specific native stat binding, none of which accept Buffer arguments.

Adds `relativeToBasePath()` and `isDirectoryPath()` helpers to
`internal/fs/utils`, alongside the existing (but previously
unexported) `join()` helper, all of which handle both string and
Buffer paths. The recursive readdir implementations in `lib/fs.js`
and `lib/internal/fs/promises.js` now use these instead of calling
`path`/the module-resolution stat binding directly.

`isDirectoryPath()` falls back to the general-purpose `stat` binding
(the same one `fs.statSync()` uses) for Buffer paths, so it also
handles non-UTF8 file names correctly instead of round-tripping
through a lossy string conversion.

Fixes: nodejs#58892
Signed-off-by: Can <hello@syntaxandco.com>
@nodejs-github-bot nodejs-github-bot added fs Issues and PRs related to the fs subsystem / file system. needs-ci PRs that need a full CI run. labels Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

all versions of readdir don't work in recursive mode when used with a buffer argument

2 participants