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
- Copy only the concrete files/subdirectories you need, excluding the path containing the symlink.
- Resolve the link yourself first (fs.realpath) and copy the real target's contents as regular files.
- Restructure the source so shared content is duplicated rather than symlinked before asking the agent to copy it.
- 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
- Pre-scan source trees with lstat before asking the agent to copy them; skip or resolve symlinks explicitly.
- Expect pnpm/yarn-workspace node_modules and system config dirs to contain links — exclude them from agent copy operations.
- Treat this guard as a security feature: don't disable or bypass it for agent-driven paths.
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
- Access denied - symlink target outside allowed directories.
- Access denied - parent directory outside allowed…
- Failed to create folder
- search pattern must not start with '-'
- Access denied - path outside allowed directories.
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)