Closed
Description
Version
v24.1.0
Platform
Linux Laptop-16-AMD-Ryzen-7040-Series 6.8.0-60-generic #63-Ubuntu SMP PREEMPT_DYNAMIC Tue Apr 15 19:04:15 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Subsystem
fs
What steps will reproduce the bug?
Run the following code:
"use strict";
const fs = require("node:fs");
const path = require("node:path");
// cleans up test and dest directories
fs.rmSync("./tmp/test", { recursive: true, force: true });
fs.rmSync("./tmp/dest", { recursive: true, force: true });
// prepares the test directory
fs.mkdirSync("./tmp/test", { recursive: true });
// in the test directory creates a source.txt file and a link.txt symlink that points to the source.txt file
fs.writeFileSync("./tmp/test/source.txt", "test content");
fs.symlinkSync(path.resolve("./tmp/test/source.txt"),"./tmp/test/link.txt");
// call cpSync
fs.cpSync("./tmp/test", "./tmp/dest", { recursive: true });
// call cpSync again (this so with dest already existing/populated)
fs.cpSync("./tmp/test", "./tmp/dest", { recursive: true });
How often does it reproduce? Is there a required condition?
the issue can always be reproduced and it does not require a specific condition
What is the expected behavior? Why is that the expected behavior?
I believe that the second cpSync
call should not throw any error, but the existing symlink files should be skipped unless force
is set to false
and errorOnExist
is true
, in such case an EEXIST
error should be thrown instead
What do you see instead?
The second cpSync
call throws an ERR_FS_CP_EINVAL
error
Additional information
-
The
ERR_FS_CP_EINVAL
error is thrown even whenforce
is being used (and the dest file is not overridden). -
This applies both to
cp
andcpSync
`cp` reproduction
"use strict"; const fs = require("node:fs"); const path = require("node:path"); fs.rmSync("./tmp/test", { recursive: true, force: true }); fs.rmSync("./tmp/dest", { recursive: true, force: true }); fs.mkdirSync("./tmp/test", { recursive: true }); fs.writeFileSync("./tmp/test/source.txt", "test content"); fs.symlinkSync(path.resolve("./tmp/test/source.txt"),"./tmp/test/link.txt"); fs.cp("./tmp/test", "./tmp/dest", { recursive: true }, (err) => { if(err) throw err; fs.cp("./tmp/test", "./tmp/dest", { recursive: true }, (err) => { if (err) throw err; }); });
- setting
dereference
totrue
seems to fix the issue, however I am not sure if dereferencing is actually taking place since the symlinks are copied as such and not as the files they point to (which I think is the expected behavior? 🤔)