Skip to content

fs: support Buffer paths in cp() and cpSync() - #65713

Open
HuzaifaAbdulRehman wants to merge 1 commit into
nodejs:mainfrom
HuzaifaAbdulRehman:fix/58634-cp-buffer-paths-filter
Open

fs: support Buffer paths in cp() and cpSync()#65713
HuzaifaAbdulRehman wants to merge 1 commit into
nodejs:mainfrom
HuzaifaAbdulRehman:fix/58634-cp-buffer-paths-filter

Conversation

@HuzaifaAbdulRehman

Copy link
Copy Markdown

What breaks

fs.cpSync() with a filter, and fs.promises.cp(), throw ERR_INVALID_ARG_TYPE when src or dest is a Buffer, though Buffer paths are accepted elsewhere in fs (they address non-UTF-8 byte file names on POSIX).

const { cpSync, mkdirSync } = require('node:fs');
mkdirSync('a/c', { recursive: true });
cpSync(Buffer.from('a'), Buffer.from('b'), { recursive: true, filter: () => true });
// TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string.
//     at join (node:path)
//     at copyDir (node:internal/fs/cp/cp-sync)

Root cause

The recursive walk builds child paths with path.join(src, name), and the async path also runs path.resolve() / path.dirname() in its structural checks. All reject a Buffer. cpSync without a filter avoids it (that branch runs in C++); cp.promises hits it in isSrcSubdir before copying.

The change

joinPath joins entries onto Buffer paths by concatenating bytes, and copyDir reads entries with encoding: 'buffer' for Buffer sources, so non-UTF-8 names survive rather than being mangled. toPathString decodes to a string only for the subdirectory and parent-directory checks; the copy keeps the Buffers.

String paths over a non-UTF-8-named entry stay unchanged (a string cannot carry those bytes) and remain a known issue, nearer #58869.

Tests

Promotes the two #58634 known-issue tests into test/parallel/ and adds a Linux-only byte-fidelity test (a Shift-JIS file name copied intact through Buffer paths).

Fixes: #58634

`fs.cpSync()` with a filter, and `fs.promises.cp()`, threw
`ERR_INVALID_ARG_TYPE` when `src` or `dest` was a Buffer. The recursive
directory walk passed the Buffer paths to `path.join()`, and the async
path additionally to `path.resolve()` and `path.dirname()`, all of which
only accept strings. The sync path only reached this on the filter branch
because the no-filter branch runs entirely in C++.

Join directory entries onto Buffer paths by concatenating bytes, read
entries with `encoding: 'buffer'` so non-UTF-8 byte file names on POSIX
are preserved, and decode Buffer paths to strings only for the structural
subdirectory and parent-directory checks.

Fixes: nodejs#58634
Assisted-by: Claude Code
Signed-off-by: Huzaifa Abdul Rehman <huzaifarehman897@gmail.com>
@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 Sep 1, 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 file-system APIs and the fs module. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fs.cpSync / fs.cp / fs.promises.cp fails when src/dest args are Buffer

2 participants