Title: cpSync fails with ENOENT when copying symlinks into non-existent directories
cpSync with recursive: true fails when the source is a symlink and the destination's parent directory doesn't exist. Regular files work fine in the same scenario.
Reproduction
const { cpSync, symlinkSync, writeFileSync, rmSync, mkdirSync } = require("fs");
const dir = "/tmp/bun-cpsync-symlink";
rmSync(dir, { recursive: true, force: true });
mkdirSync(dir);
process.chdir(dir);
writeFileSync("target.md", "# Hello");
symlinkSync("target.md", "link.md");
// Regular file → works
cpSync("target.md", "out/target.md", { recursive: true });
// Symlink → ENOENT
cpSync("link.md", "out2/link.md", { recursive: true });
ENOENT: no such file or directory, copyfile 'link.md'
Works fine with Node 25. Also works in Bun if you pre-create the parent dir, or pass dereference: true (which forces the JS fallback path).
Cause
The native Zig implementation in _copySingleFileSync (node_fs.zig) already handles ENOENT for regular files by creating parent directories and retrying. The symlink code paths skip this:
- macOS (~L6296): calls
copyfile() with COPYFILE_NOFOLLOW_SRC, returns immediately on error
- Linux (~L6410): calls
symlink(src, dest) directly, no parent dir creation
The JS fallback (cp-sync.ts) doesn't have this problem because checkParentDir() runs for all source types before dispatching.
Version
Bun 1.3.11, macOS 15.5 (arm64). Likely affects all platforms.
Title: cpSync fails with ENOENT when copying symlinks into non-existent directories
cpSyncwithrecursive: truefails when the source is a symlink and the destination's parent directory doesn't exist. Regular files work fine in the same scenario.Reproduction
Works fine with Node 25. Also works in Bun if you pre-create the parent dir, or pass
dereference: true(which forces the JS fallback path).Cause
The native Zig implementation in
_copySingleFileSync(node_fs.zig) already handles ENOENT for regular files by creating parent directories and retrying. The symlink code paths skip this:copyfile()withCOPYFILE_NOFOLLOW_SRC, returns immediately on errorsymlink(src, dest)directly, no parent dir creationThe JS fallback (
cp-sync.ts) doesn't have this problem becausecheckParentDir()runs for all source types before dispatching.Version
Bun 1.3.11, macOS 15.5 (arm64). Likely affects all platforms.