denoland/deno · error · NodeSystemError

ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY

ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY

Error message

Cannot overwrite symlink in subdirectory of self

What it means

ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY ('Cannot overwrite symlink in subdirectory of self') is thrown for kind 'SYMLINK_TO_SUBDIRECTORY': with dereference:true, the resolved destination lands inside the source tree (dest is a subdirectory of src), so continuing would make cp copy a directory into itself. It is the same guard Node uses to prevent infinite recursive copies.

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

Solutions

  1. Choose a destination outside the source tree
  2. Or set dereference:false / verbatimSymlinks as appropriate so dest resolution does not fold into src
  3. Guard with path.relative: if the result doesn't start with '..' and isn't '', dest is inside src — reject before copying

Example fix

// before
await fsp.cp(src, path.join(src, 'dist/backup'), { recursive: true, dereference: true });

// after
const dest = path.join(rootOutsideSrc, 'backup');
await fsp.cp(src, dest, { recursive: true, dereference: true });
Defensive patterns

Strategy: validation

Validate before calling

const rel = path.relative(path.resolve(src), path.resolve(dest));
const destInsideSrc = rel !== '' && !rel.startsWith('..');
if (destInsideSrc) throw new Error('dest must not be inside src when dereference is enabled');

Type guard

function isDestInsideSrc(src, dest) {
  const rel = path.relative(path.resolve(src), path.resolve(dest));
  return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);
}

Try / catch

try { await fsp.cp(src, dest, { recursive: true, dereference: true }); } catch (e) { if (e.code === 'ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY') throw new Error(`dest ${dest} is inside src ${src}`); throw e; }

Prevention

When it happens

Trigger: await fsp.cp('/data/project', '/data/project/sub/copy', { recursive: true, dereference: true }); copying a folder into a nested folder of itself; build tools writing output into an input subfolder while following symlinks.

Common situations: Out-of-tree output paths misconfigured to live under the source tree; monorepo scripts whose dest is computed relative to src and ends up nested.

Related errors


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