Mintplex-Labs/anything-llm · error · Error
Access denied - path outside allowed directories.
Error message
Access denied - path outside allowed directories.
What it means
Thrown by FileOperationsLib.validatePath (server/utils/agents/aibitat/plugins/filesystem/lib.js:429) when the requested path, after tilde expansion and normalization, does not fall under any allowed directory. Allowed directories default to <STORAGE_DIR>/anythingllm-fs (or <repo>/storage/anythingllm-fs) unless the library is initialized with an explicit list. This is the sandbox boundary checked on every agent filesystem tool call (read, write, list, edit, search).
Solutions
- Call list_allowed_directories (getAllowedDirectories()) and rewrite the target path under one of the returned roots; relative paths resolve against the first allowed directory.
- If the file legitimately lives elsewhere, copy it into the allowed root first, or initialize the FileOperationsLib with additional allowed directories.
- Verify STORAGE_DIR and that ANYTHING_LLM_RUNTIME=docker (or NODE_ENV=development) so the tool and its default root exist as expected.
- Check the server console log line '[validatePath] Access denied - path outside allowed directories: <abs> not in <roots>' to see both sides of the mismatch.
Example fix
// before (agent tool call)
read_file({ path: "/tmp/notes.txt" }) // outside sandbox -> throws
// after: relative path resolves against the first allowed directory
read_file({ path: "notes.txt" })
// or explicitly:
read_file({ path: "/app/storage/anythingllm-fs/notes.txt" }) Defensive patterns
Strategy: validation
Validate before calling
const path = require("path");
const os = require("os");
const expandHome = (p) => (p.startsWith("~") ? path.join(os.homedir(), p.slice(1)) : p);
function isPathAllowed(fileOps, requestedPath) {
const abs = path.resolve(expandHome(requestedPath));
return fileOps.getAllowedDirectories().some(
(root) => abs === root || abs.startsWith(root + path.sep)
);
}
if (!isPathAllowed(fileOps, target)) {
target = path.join(fileOps.getAllowedDirectories()[0], path.basename(target));
} Type guard
/** @param {string} p */
function isSandboxPath(fileOps, p) {
if (typeof p !== "string" || p.length === 0) return false;
return isPathAllowed(fileOps, p); // boolean narrow before any tool call
} Try / catch
try {
await fileOps.readFileContent(target);
} catch (e) {
if (e.message.includes("outside allowed directories")) {
// sandbox denial: re-prompt the agent with the allowed roots, never widen the sandbox silently
throw new Error(`Path denied. Allowed roots: ${fileOps.getAllowedDirectories().join(", ")}`);
}
throw e;
} Prevention
- Always resolve agent-supplied paths against getAllowedDirectories() before invoking a filesystem tool.
- Prefer relative paths in prompts; the library resolves them under the first allowed directory.
- Fix STORAGE_DIR once per deployment so the default sandbox root never moves.
- Treat repeated denials for host paths as a prompt-design issue: tell the agent which root is writable.
When it happens
Trigger: Calling any filesystem tool with an absolute path outside the allowed roots (e.g. /tmp/notes.txt when only .../storage/anythingllm-fs is allowed); passing a relative path that does not resolve under an allowed directory; the agent reusing an absolute path from a previous container/host where STORAGE_DIR differed; changing STORAGE_DIR between runs so the default root moves.
Common situations: Agent tries to read /etc/passwd or ~/.ssh/id_rsa outside the sandbox; developer runs outside Docker (isToolAvailable only returns true for docker runtime or NODE_ENV=development); STORAGE_DIR env var repointed so previously valid paths now fail; user pastes a host path instead of a workspace-relative path.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied - parent directory outside allowed…
- Access denied - symlink target outside allowed directories.
- Could not find exact match for edit:\n
- Parent directory does not exist
- Cannot copy symbolic link
AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18).
Data as JSON: /api/errors/19b2395317182214.
Report an issue: GitHub.
Appendix: source
Thrown at server/utils/agents/aibitat/plugins/filesystem/lib.js:429
*/
async validatePath(requestedPath) {
await this.ensureInitialized();
const expandedPath = this.#expandHome(requestedPath);
const absolute = path.isAbsolute(expandedPath)
? path.resolve(expandedPath)
: this.#resolveRelativePathAgainstAllowedDirectories(expandedPath);
const normalizedRequested = this.#normalizePath(absolute);
const isAllowed = this.#isPathWithinAllowedDirectories(
normalizedRequested,
this.#allowedDirectories
);
if (!isAllowed) {
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.`
);
}View on GitHub (pinned to 3aec848f28)