denoland/deno · error · SystemError

ERR_FS_CP_EINVAL

ERR_FS_CP_EINVAL

Error message

Invalid src or dest

What it means

fs.cp(src, dest) runs a native path check that can reject the operation with CpError::EInval (kind 'EINVAL'), which throwCpError converts into ERR_FS_CP_EINVAL (the class's default message is 'Invalid src or dest'; the actual message comes from the native check). Two conditions produce it in ext/node/ops/fs.rs: src and dest are the same file (identical dev/ino stats — 'src and dest cannot be the same'), and recursive copies where dest lies inside src ('cannot copy X to a subdirectory of self Y').

Solutions

  1. Resolve both paths with path.resolve and assert they differ before calling fs.cp
  2. Make dest a sibling, not a child: fs.cp('proj', '../proj-copy', { recursive: true })
  3. Use fs.realpath on both sides to catch symlink/hardlink identity, not just string equality

Example fix

// before
fs.cp('proj', 'proj/dist/backup', { recursive: true }, cb); // dest inside src

// after
fs.cp('proj', 'backup', { recursive: true }, cb); // sibling destination
Defensive patterns

Strategy: try-catch

Validate before calling

const a = fs.realpathSync(src), b = fs.realpathSync(dest);
if (a === b || b.startsWith(a + path.sep)) throw new Error('cp: dest must not equal or live inside src');

Try / catch

try { await fs.promises.cp(src, dest, opts); } catch (e) { if (e.code === 'ERR_FS_CP_EINVAL') { /* fix paths, never retry unchanged */ } else throw e; }

Prevention

When it happens

Trigger: fs.cp('/a/f.txt', '/a/f.txt') or two hardlinks/symlinks resolving to the same inode; fs.cp('proj', 'proj/copy', { recursive: true }) — copying a directory into itself; a symlinked dest that resolves back to the src directory.

Common situations: Build scripts copying a directory into its own subtree (dist inside src); templating code where src and dest strings differ but resolve to the same path; hardlinked cache files used as both source and target.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/fd07a4027482aece. Report an issue: GitHub.

Appendix: source

Thrown at ext/node/polyfills/_fs/cp/cp.ts:60

  IsSrcFile: 1n << 34n,
  IsSrcCharDevice: 1n << 35n,
  IsSrcBlockDevice: 1n << 36n,
  IsSrcSymlink: 1n << 37n,
  IsSrcSocket: 1n << 38n,
  IsSrcFifo: 1n << 39n,
};

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,

View on GitHub (pinned to 9ad36f7a2c)