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

  1. Copy to a destination outside the source tree entirely (sibling or temp dir, then move)
  2. Break the self-referential symlink chain: resolve dest with fs.realpath and reject paths that start with the resolved src prefix
  3. 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

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)