denoland/deno · error · NodeSystemError

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

ERR_FS_CP_DIR_TO_NON_DIR is thrown by throwCpError when the native cp validation reports kind 'DIR_TO_NON_DIR': the source is a directory but the existing destination is a non-directory (a file), so the copy would have to overwrite a file with a directory tree, which fs.cp refuses.

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 89f33cbef2)

Solutions

  1. Remove or rename the existing file at dest first (fs.rm(dest, { force: true }))
  2. Point dest at a directory path that does not currently exist as a file
  3. If automated, pre-stat dest and clean conflicting non-directories before copying

Example fix

// before
await fsp.cp(srcDir, destPath, { recursive: true }); // destPath is an existing file

// after
const st = await fsp.lstat(destPath).catch(() => null);
if (st && !st.isDirectory()) await fsp.rm(destPath, { force: true });
await fsp.cp(srcDir, destPath, { recursive: true });
Defensive patterns

Strategy: validation

Validate before calling

const st = await fsp.lstat(dest).catch(() => null);
if (st && !st.isDirectory()) await fsp.rm(dest, { force: true });
await fsp.cp(src, dest, { recursive: true });

Type guard

async function isDir(p) { try { return (await fsp.lstat(p)).isDirectory(); } catch { return false; } }

Try / catch

try { await fsp.cp(src, dest, { recursive: true }); } catch (e) { if (e.code === 'ERR_FS_CP_DIR_TO_NON_DIR') { await fsp.rm(dest, { force: true }); await fsp.cp(src, dest, { recursive: true }); } else throw e; }

Prevention

When it happens

Trigger: await fsp.cp('./src', './src.txt', { recursive: true }) where src is a directory and src.txt an existing regular file; deploying a directory over a stale file left at the destination path.

Common situations: Build outputs where an earlier run produced a file and a later run produces a directory at the same path; wrong CLI arg order swapping a file dest and dir src.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/61a5d2c684424f71. Report an issue: GitHub.