Mintplex-Labs/anything-llm · error · Error
Access denied - symlink target outside allowed directories.
Error message
Access denied - symlink target outside allowed directories.
What it means
Thrown by validatePath when fs.realpath() resolves the requested path (via symlink) to a physical location outside the allowed directories. Even though the requested path string sits inside the sandbox, the operating system would actually read/write the link target, so the library rejects it. This blocks symlink-escape attacks against the agent filesystem sandbox.
Solutions
- Remove or replace the symlink with a real copy of the target inside the allowed directory.
- If the target location must be accessible, initialize the filesystem library with the real target directory added to the allowed list.
- Inspect with ls -la / readlink -f <path> to confirm which link escapes, then fix that specific link.
Example fix
# before
ln -s /var/reports /app/storage/anythingllm-fs/reports
read_file({ path: "reports/q3.txt" }) # throws: symlink target outside
# after
cp -r /var/reports /app/storage/anythingllm-fs/reports
read_file({ path: "reports/q3.txt" }) # ok Defensive patterns
Strategy: try-catch
Validate before calling
const fs = require("fs").promises;
const path = require("path");
async function assertNoSymlinkEscape(fileOps, target) {
const allowed = fileOps.getAllowedDirectories();
const real = await fs.realpath(target).catch(() => null);
if (real && !allowed.some((r) => real === r || real.startsWith(r + path.sep))) {
throw new Error(`Refusing ${target}: symlink resolves to ${real}, outside sandbox`);
}
} Try / catch
try {
const validated = await fileOps.validatePath(p);
} catch (e) {
if (e.message.includes("symlink target outside allowed directories")) {
// security event: log path + realpath, reject; do not auto-retry or rewrite the path
securityLog(`symlink escape attempt: ${p}`);
return;
}
throw e;
} Prevention
- Audit the sandbox root for symlinks before granting the agent access: find <root> -type l -exec readlink -f {} \;
- Replace symlinks with copies or bind-mounts that resolve inside the sandbox.
- Never auto-add a symlink's target to allowed directories in response to this error without human review.
When it happens
Trigger: A file inside <storage>/anythingllm-fs is a symlink to /etc, /root, or any directory outside the allowed roots; agent writes to a symlinked path whose target is on another mount; a previously-clean path becomes a symlink after a package or setup step creates links in the workspace.
Common situations: User symlinks a folder into the sandbox to 'share' documents with the agent; ln -s /var/data ./storage/anythingllm-fs/data in a Docker volume setup; CI copies a tree that contains absolute symlinks that only resolve on the build host.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied - parent directory outside allowed…
- Cannot copy symbolic link
- Access denied - path outside allowed directories.
- search pattern must not start with '-'
- AGENT_KEENABLE_API_URL must use https:// (or target a…
AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18).
Data as JSON: /api/errors/729782da75dd8bd8.
Report an issue: GitHub.
Appendix: source
Thrown at server/utils/agents/aibitat/plugins/filesystem/lib.js:444
console.log(
`[validatePath] Access denied - path outside allowed directories: ${absolute} not in ${this.#allowedDirectories.join(", ")}`
);
throw new Error(`Access denied - path outside allowed directories.`);
}
try {
const realPath = await fs.realpath(absolute);
const normalizedReal = this.#normalizePath(realPath);
if (
!this.#isPathWithinAllowedDirectories(
normalizedReal,
this.#allowedDirectories
)
) {
console.log(
`[validatePath] Access denied - symlink target outside allowed directories: ${realPath} not in ${this.#allowedDirectories.join(", ")}`
);
throw new Error(
`Access denied - symlink target outside allowed directories.`
);
}
return realPath;
} catch (error) {
if (error.code === "ENOENT") {
const parentDir = path.dirname(absolute);
try {
const realParentPath = await fs.realpath(parentDir);
const normalizedParent = this.#normalizePath(realParentPath);
if (
!this.#isPathWithinAllowedDirectories(
normalizedParent,
this.#allowedDirectories
)
) {
console.log(
`[validatePath] Access denied - parent directory outside allowed directories: ${realParentPath} not in ${this.#allowedDirectories.join(", ")}`View on GitHub (pinned to 3aec848f28)