denoland/deno · error · SystemError
ERR_FS_CP_DIR_TO_NON_DIR
ERR_FS_CP_DIR_TO_NON_DIR
Error message
Cannot overwrite non-directory with directory
What it means
During fs.cp(src, dest), the native path check stats both entries; if src is a directory but an existing dest is not a directory, the copy is rejected with kind DIR_TO_NON_DIR, which throwCpError maps to ERR_FS_CP_DIR_TO_NON_DIR ('Cannot overwrite non-directory with directory', errno EISDIR). Node refuses because writing the directory's entries onto a regular file would corrupt the destination.
Solutions
- Check dest first: if it exists and is a file, unlink or rename it before the directory copy
- Use a fresh destination path or clean the target directory between runs (rm -rf && cp)
- Guard with fs.stat and branch: copy dir-vs-dir or file-vs-file deliberately
Example fix
// before
fs.cp('dist/', 'release', { recursive: true }, cb); // 'release' is an existing file
// after
const st = fs.statSync('release', { throwIfNoEntry: false });
if (st?.isFile()) fs.rmSync('release');
fs.cp('dist/', 'release', { recursive: true }, cb); Defensive patterns
Strategy: try-catch
Validate before calling
const st = fs.statSync(dest, { throwIfNoEntry: false });
if (st && !st.isDirectory()) throw new Error('cp: dest is a file but src is a directory'); Try / catch
try { await fs.promises.cp(srcDir, dest, { recursive: true }); } catch (e) { if (e.code === 'ERR_FS_CP_DIR_TO_NON_DIR') { fs.rmSync(dest); return fs.promises.cp(srcDir, dest, { recursive: true }); } throw e; } Prevention
- Clean stale placeholder files before directory copies
- Assert kind compatibility (dir-to-dir, file-to-file) in deploy scripts
- Treat marker/touch files as cache invalidation, not copy targets
When it happens
Trigger: fs.cp('src-dir/', 'existing-file.txt', { recursive: true }) — recursing a directory onto a plain file; dest previously created as an empty touch-file placeholder (common in provisioning scripts) that a later directory copy targets.
Common situations: Deploy scripts that 'reserve' an output path by touching a file, then copy a directory to it; switching a copy target from file to directory between releases without cleaning the workspace; cache dirs whose marker file was left behind.
Related errors
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/61a5d2c684424f71.
Report an issue: GitHub.
Appendix: source
Thrown at ext/node/polyfills/_fs/cp/cp.ts:68
const {
Number,
PromiseResolve,
SymbolAsyncIterator,
} = primordials;
function throwCpError(err) {
const { EEXIST, EINVAL, EISDIR, ENOTDIR } = lazyConstants();
switch (err.kind) {
case "EINVAL":
throw new ERR_FS_CP_EINVAL({
message: err.message,
path: err.path,
syscall: "cp",
errno: EINVAL,
code: "EINVAL",
});
case "DIR_TO_NON_DIR":
throw new ERR_FS_CP_DIR_TO_NON_DIR({
message: err.message,
path: err.path,
syscall: "cp",
errno: EISDIR,
code: "EISDIR",
});
case "NON_DIR_TO_DIR":
throw new ERR_FS_CP_NON_DIR_TO_DIR({
message: err.message,
path: err.path,
syscall: "cp",
errno: ENOTDIR,
code: "ENOTDIR",
});
case "EEXIST":
throw new ERR_FS_CP_EEXIST({
message: err.message,
path: err.path,View on GitHub (pinned to 9ad36f7a2c)