denoland/deno · error · ERR_FS_CP_SOCKET

EINVAL

EINVAL

Error message

cannot copy a socket file: ${dest}

What it means

Thrown by the JS slow path of Deno's cp polyfill when a copied entry is a unix-domain socket: 'cannot copy a socket file: <dest>' with code EINVAL (cp.ts:204-211). It fires during recursive copies whose options (filter, dereference, force: false, errorOnExist, preserveTimestamps, verbatimSymlinks, nonzero mode) disable the native fast path — see canUseNativeFastPath at cp.ts:168-176. The Rust fast path raises the equivalent ERR_FS_CP_SOCKET instead.

Source

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

      errno: EISDIR,
      code: "EISDIR",
    });
  } else if (
    (statInfo & CpEntryFlags.IsSrcFile) ||
    (statInfo & CpEntryFlags.IsSrcCharDevice) ||
    (statInfo & CpEntryFlags.IsSrcBlockDevice)
  ) {
    return onFile(statInfo, src, dest, opts);
  } else if (statInfo & CpEntryFlags.IsSrcSymlink) {
    const isDestExists = !!(statInfo & CpEntryFlags.IsDestExists);
    return onLink(isDestExists, src, dest, opts);
  } else if (statInfo & CpEntryFlags.IsSrcSocket) {
    throw new ERR_FS_CP_SOCKET({
      message: `cannot copy a socket file: ${dest}`,
      path: dest,
      syscall: "cp",
      errno: EINVAL,
      code: "EINVAL",
    });
  } else if (statInfo & CpEntryFlags.IsSrcFifo) {
    throw new ERR_FS_CP_FIFO_PIPE({
      message: `cannot copy a FIFO pipe: ${dest}`,
      path: dest,
      syscall: "cp",
      errno: EINVAL,
      code: "EINVAL",
    });
  }
  throw new ERR_FS_CP_UNKNOWN({
    message: `cannot copy an unknown file type: ${dest}`,
    path: dest,
    syscall: "cp",
    errno: EINVAL,
    code: "EINVAL",
  });
}

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Extend your filter to skip sockets: inside filter, return false when (await lstat(src)).isSocket().
  2. Name-based guard as a cheap alternative: filter: (s) => !s.endsWith('.sock').
  3. Scope the copy to the data subdirectory instead of the runtime root that holds sockets.

Example fix

// before
await fs.promises.cp('/tmp/app', '/backup/app', {
  recursive: true,
  filter: (s) => !s.includes('cache'),
});
// EINVAL: cannot copy a socket file: /backup/app/run.sock

// after
await fs.promises.cp('/tmp/app', '/backup/app', {
  recursive: true,
  filter: async (s, d) =>
    !s.includes('cache') && !(await fs.promises.lstat(s)).isSocket(),
});
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
await fs.promises.cp(srcDir, destDir, {
  recursive: true,
  filter: async (p) => {
    const st = await fs.promises.lstat(p);
    return !st.isSocket() && !st.isFIFO();
  },
});

Try / catch

try {
  await fs.promises.cp(a, b, { recursive: true, dereference: true });
} catch (err) {
  if (err.code === 'EINVAL' && /cannot copy a socket file/.test(err.message)) {
    // rerun with a socket-skipping filter
  } else throw err;
}

Prevention

When it happens

Trigger: fs.cp('/tmp', backup, { recursive: true, dereference: true }) walking into .X11-unix sockets; recursive filtered copies of runtime dirs containing docker.sock or an app's listening socket.

Common situations: Dev tooling that backs up project tmp/ dirs (Rails tmp/sockets/puma.sock); sandbox copies that include X11 or dbus sockets; docker-in-docker scripts copying /var/run.

Related errors


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