Mintplex-Labs/anything-llm · error · Error

Cannot copy symbolic link

Error message

Cannot copy symbolic link: ${source}. Symlinks are not allowed during copy operations.

What it means

Thrown by copyRecursive in the agent filesystem copy-file plugin when fs.lstat identifies the source path as a symbolic link. This is a deliberate security guard: recursively copying attacker-controlled trees that may contain symlinks enables path-traversal/arbitrary-file-write attacks when an agent copies into the workspace, so symlinks abort the whole copy instead of being followed or preserved.

Solutions

  1. Copy only the concrete files/subdirectories you need, excluding the path containing the symlink.
  2. Resolve the link yourself first (fs.realpath) and copy the real target's contents as regular files.
  3. Restructure the source so shared content is duplicated rather than symlinked before asking the agent to copy it.
  4. Treat this abort as by-design: do not attempt to bypass it in agent-driven flows.

Example fix

// before: agent tool call
copyFile({ source: "/data/project", destination: "/data/backup" })
// /data/project contains a symlink -> Error: Cannot copy symbolic link...

// after: copy only real entries
const fs = require("fs/promises");
for (const entry of await fs.readdir("/data/project", { withFileTypes: true })) {
  if (entry.isSymbolicLink()) continue; // skip links
  await fs.cp(`/data/project/${entry.name}`, `/data/backup/${entry.name}`, { recursive: true });
}
Defensive patterns

Strategy: validation

Validate before calling

const fs = require("fs/promises");

async function containsSymlink(p) {
  const st = await fs.lstat(p);
  if (st.isSymbolicLink()) return true;
  if (!st.isDirectory()) return false;
  for (const entry of await fs.readdir(p)) {
    if (await containsSymlink(require("path").join(p, entry))) return true;
  }
  return false;
}

if (await containsSymlink(sourceDir))
  throw new Error("Refusing to copy: source tree contains a symlink");

Try / catch

try {
  await copyFileTool({ source, destination });
} catch (e) {
  if (/Cannot copy symbolic link/.test(e.message)) {
    // skip links and copy only real entries, or resolve the target manually
    return copyRealEntriesOnly(source, destination);
  }
  throw e;
}

Prevention

When it happens

Trigger: An agent using the copy-file filesystem tool on any directory tree that contains a symlink — node_modules with linked packages, /usr/sharealternatives-style links, dotfile directories with links, or a user deliberately symlink-ing shared assets into the copy source.

Common situations: Asking the agent to copy a project folder containing node_modules (pnpm/yarn workspaces use symlinks heavily); copying configuration trees where symlinks are routine; copying from a mounted volume whose contents include host-created links.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/a2aa67b8e5a87203. Report an issue: GitHub.

Appendix: source

Thrown at server/utils/agents/aibitat/plugins/filesystem/copy-file.js:9

const fs = require("fs/promises");
const path = require("path");
const filesystem = require("./lib.js");

async function copyRecursive(source, destination) {
  const lstat = await fs.lstat(source);

  if (lstat.isSymbolicLink()) {
    throw new Error(
      `Cannot copy symbolic link: ${source}. Symlinks are not allowed during copy operations.`
    );
  }

  if (lstat.isDirectory()) {
    await fs.mkdir(destination, { recursive: true });
    const entries = await fs.readdir(source);
    for (const entry of entries) {
      await copyRecursive(
        path.join(source, entry),
        path.join(destination, entry)
      );
    }
  } else {
    await fs.copyFile(source, destination);
  }
}

View on GitHub (pinned to 3aec848f28)