modelcontextprotocol/servers · error · Error
Access denied - symlink target outside allowed directories:
Error message
Access denied - symlink target outside allowed directories: ${realPath} not in ${allowedDirectories.join(', ')} What it means
Thrown by `validatePath` after resolving the realpath of an existing path, when the symlink's real target lies outside the allowed directories. This is the anti-symlink-attack guard: even if the requested path text is inside the allowlist, a symlink within it could point elsewhere, so the server resolves `fs.realpath` and re-checks the boundary.
Source
Thrown at src/filesystem/lib.ts:119
const absolute = path.isAbsolute(expandedPath)
? path.resolve(expandedPath)
: resolveRelativePathAgainstAllowedDirectories(expandedPath);
const normalizedRequested = normalizePath(absolute);
// Security: Check if path is within allowed directories before any file operations
const isAllowed = isPathWithinAllowedDirectories(normalizedRequested, allowedDirectories);
if (!isAllowed) {
throw new Error(`Access denied - path outside allowed directories: ${absolute} not in ${allowedDirectories.join(', ')}`);
}
// Security: Handle symlinks by checking their real path to prevent symlink attacks
// This prevents attackers from creating symlinks that point outside allowed directories
try {
const realPath = await fs.realpath(absolute);
const normalizedReal = normalizePath(realPath);
if (!isPathWithinAllowedDirectories(normalizedReal, allowedDirectories)) {
throw new Error(`Access denied - symlink target outside allowed directories: ${realPath} not in ${allowedDirectories.join(', ')}`);
}
return realPath;
} catch (error) {
// Security: For new files that don't exist yet, verify parent directory
// This ensures we can't create files in unauthorized locations
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
const parentDir = path.dirname(absolute);
try {
const realParentPath = await fs.realpath(parentDir);
const normalizedParent = normalizePath(realParentPath);
if (!isPathWithinAllowedDirectories(normalizedParent, allowedDirectories)) {
throw new Error(`Access denied - parent directory outside allowed directories: ${realParentPath} not in ${allowedDirectories.join(', ')}`);
}
return absolute;
} catch {
throw new Error(`Parent directory does not exist: ${parentDir}`);
}
}View on GitHub (pinned to 76d64c822f)
Solutions
- Either remove the offending symlink or point it at a target inside the allowed directories.
- Add the symlink's real target directory to the allowed directories (CLI args or roots), understanding the security implication.
- Audit symlinks under allowed dirs before granting access: `find <dir> -type l -exec readlink {} \;`.
Example fix
# before: /home/me/projects/link -> /etc/secrets (outside allowlist) # after: re-point the link inside an allowed dir, OR add the target's parent to allowlist ln -sfn /home/me/shared/data /home/me/projects/link
Defensive patterns
Strategy: try-catch
Validate before calling
import fs from 'node:fs/promises';
async function symlinkTargetInside(p: string, allowed: string[]): Promise<boolean> {
try {
const real = await fs.realpath(p);
return allowed.some(d => real.startsWith(path.resolve(d) + path.sep) || real === path.resolve(d));
} catch { return true; /* let the server decide on missing paths */ }
} Try / catch
try {
await readFile({ path });
} catch (e) {
if (e instanceof Error && e.message.startsWith('Access denied - symlink target')) {
// remove/repoint the symlink or add its target's parent to allowed dirs
}
} Prevention
- Audit symlinks under allowed dirs: find <dir> -type l.
- Don't create symlinks that escape the sandbox.
- If a cross-dir symlink is legitimate, allowlist the real target dir.
When it happens
Trigger: A file or directory inside an allowed dir is a symlink whose target resolves outside the allowlist — e.g. `/home/me/projects/link -> /etc/secrets`. The requested path passes the first check (error 16 path) but fails the realpath check.
Common situations: Pre-existing symlinks in the workspace pointing outside, a compromised/attacker-created symlink inside an allowed dir, or legitimate cross-dir symlinks (e.g. to a shared lib) that the operator forgot to allowlist.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied - parent directory outside allowed directories
- Access denied - path outside allowed directories: ${absolute
- Server cannot operate: No allowed directories available. Ser
- Unsupported URL protocol for ${dataUri}. Only http, https, a
- Domain ${domain} is not in the allowed domains list.
AI-assisted analysis of modelcontextprotocol/servers@76d64c822f (2026-08-12).
Data as JSON: /api/errors/632e004ee3015652.
Report an issue: GitHub.