denoland/deno · error · SystemError
ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY
ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY
Error message
Cannot overwrite symlink in subdirectory of self
What it means
When fs.cp processes a symlink whose resolution would make dest a subdirectory of src (specifically, resolved dest lies inside resolved src), the native guard rejects with CpError::SymlinkToSubdirectory (kind 'SYMLINK_TO_SUBDIRECTORY'), mapped by throwCpError to ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY ('Cannot overwrite symlink in subdirectory of self', errno EINVAL). The guard exists because unlinking/overwriting such a dest would delete the source's own contents or leave a broken symlink loop — recursive copies must not destroy their input.
Solutions
- Copy to a destination outside the source tree entirely (sibling or temp dir, then move)
- Break the self-referential symlink chain: resolve dest with fs.realpath and reject paths that start with the resolved src prefix
- Use verbatimSymlinks where appropriate so symlinks are recreated as-is instead of being followed into the source tree
Example fix
// before
fs.cp('proj', 'proj/dist/backup', { recursive: true }, cb); // resolves into self
// after
fs.cp('proj', fs.mkdtempSync('/tmp/copy-'), { recursive: true }, cb); // outside src Defensive patterns
Strategy: try-catch
Validate before calling
const s = fs.realpathSync(src), d = fs.realpathSync(dest);
if (d === s || d.startsWith(s + path.sep)) throw new Error('cp: dest resolves inside src'); Try / catch
try { await fs.promises.cp(src, dest, { recursive: true }); } catch (e) { if (e.code === 'ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY') { /* choose a dest outside the src tree and rerun */ } else throw e; } Prevention
- Copy to destinations outside the source tree, then move
- With pnpm-style symlink layouts, copy with verbatimSymlinks or tar first
- Check resolved (realpath) containment, not raw string prefixes
When it happens
Trigger: fs.cp('proj', 'proj/dist/vendor/proj-link', { recursive: true }) where the dest chain passes through a symlink back into proj; symlink farms (node_modules symlinking packages into the tree being copied) where the copy target resolves inside the copy source; tooling that copies a monorepo into one of its own packages' build outputs.
Common situations: pnpm-style symlink layouts being recursively copied; bundlers writing into a path that symlinks back to the source tree; CI artifact steps that copy the workspace into workspace/target/ while target contains symlinks to sources.
Related errors
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/afadfc2198643b75.
Report an issue: GitHub.
Appendix: source
Thrown at ext/node/polyfills/_fs/cp/cp.ts:124
});
case "FIFO":
throw new ERR_FS_CP_FIFO_PIPE({
message: err.message,
path: err.path,
syscall: "cp",
errno: EINVAL,
code: "EINVAL",
});
case "UNKNOWN":
throw new ERR_FS_CP_UNKNOWN({
message: err.message,
path: err.path,
syscall: "cp",
errno: EINVAL,
code: "EINVAL",
});
case "SYMLINK_TO_SUBDIRECTORY":
throw new ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY({
message: err.message,
path: err.path,
syscall: "cp",
errno: EINVAL,
code: "EINVAL",
});
default:
throw err;
}
}
async function cpFn(
src,
dest,
opts,
) {
try {
if (canUseNativeFastPath(opts)) {View on GitHub (pinned to 9ad36f7a2c)