Mintplex-Labs/anything-llm · error · Error

Access denied - parent directory outside allowed…

Error message

Access denied - parent directory outside allowed directories.

What it means

Thrown by validatePath when the target file does not exist yet (ENOENT from realpath) and the realpath of its parent directory resolves outside the allowed directories. This branch guards file-creation paths: before creating a new file, the library verifies the directory that will hold it is inside the sandbox after resolving symlinks. It is the parent-directory analogue of the symlink-escape check.

Solutions

  1. Resolve the parent chain with readlink -f <parent-dir> and confirm the physical path is under an allowed directory; remove the offending symlink in the parent chain.
  2. Move the real directory into the sandbox and use its physical path for writes.
  3. If the physical location must be used, add it to the library's allowed directories at initialization.

Example fix

# before
ln -s /mnt/hostdata /app/storage/anythingllm-fs/out
write_file({ path: "out/new.txt", content: "x" })  # throws

# after (real directory inside the sandbox)
mv /mnt/hostdata /app/storage/anythingllm-fs/out
write_file({ path: "out/new.txt", content: "x" })  # ok
Defensive patterns

Strategy: try-catch

Validate before calling

const fs = require("fs").promises;
const path = require("path");
async function parentResolvesInsideSandbox(fileOps, target) {
  const allowed = fileOps.getAllowedDirectories();
  const parent = path.dirname(path.resolve(target));
  const realParent = await fs.realpath(parent); // throws if parent missing -> error 323 instead
  return allowed.some((r) => realParent === r || realParent.startsWith(r + path.sep));
}

Try / catch

try {
  await fileOps.writeFileContent(p, content);
} catch (e) {
  if (e.message.includes("parent directory outside allowed directories")) {
    // resolve and inspect path.dirname chain; fix symlinks, do not widen the sandbox automatically
    throw new Error(`Write target's parent resolves outside sandbox: ${p}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: write_file to sandbox-dir/link/new.txt where 'link' is a symlink whose realpath is outside the allowed roots; creating a file under a mount point that resolves (via symlink or bind) to a host path outside STORAGE_DIR/anythingllm-fs; Docker volume layouts where the workspace dir is itself a link to /host/mnt.

Common situations: Docker-compose mounts a host directory through a symlinked path; users 'relocate' the sandbox by symlinking storage/anythingllm-fs elsewhere; NFS/automount paths whose realpath differs from the mount point.

Understand the failure class

Related errors


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

Appendix: source

Thrown at server/utils/agents/aibitat/plugins/filesystem/lib.js:464

        );
      }
      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(", ")}`
            );
            throw new Error(
              `Access denied - parent directory outside allowed directories.`
            );
          }
          return absolute;
        } catch {
          throw new Error(`Parent directory does not exist: ${parentDir}`);
        }
      }
      throw error;
    }
  }

  /**
   * Gets detailed file statistics.
   * @param {string} filePath - Path to the file
   * @returns {Promise<Object>} File statistics
   */
  async getFileStats(filePath) {

View on GitHub (pinned to 3aec848f28)