modelcontextprotocol/servers · error · Error
Access denied - parent directory outside allowed directories
Error message
Access denied - parent directory outside allowed directories: ${realParentPath} not in ${allowedDirectories.join(', ')} What it means
Thrown by `validatePath` in the ENOENT branch (new file that doesn't yet exist) when the to-be-created file's parent directory resolves — via realpath — outside the allowed directories. Because the file itself has no realpath yet, the server validates its parent dir to prevent creating files in unauthorized locations through symlinks or escaping paths.
Source
Thrown at src/filesystem/lib.ts:131
// 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}`);
}
}
throw error;
}
}
// File Operations
export async function getFileStats(filePath: string): Promise<FileInfo> {
const stats = await fs.stat(filePath);
return {
size: stats.size,
created: stats.birthtime,
modified: stats.mtime,View on GitHub (pinned to 76d64c822f)
Solutions
- Write to a path whose parent directory is inside the allowed directories (by realpath).
- Add the real parent directory to the allowed directories if the write is legitimate.
- Resolve and normalize the intended target client-side to confirm it stays inside the sandbox.
Example fix
// before (parent resolves outside allowlist)
write_file({ path: '/home/me/projects/../../tmp/x.txt', content: '...' })
// after
write_file({ path: '/home/me/projects/x.txt', content: '...' }) Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs/promises';
async function parentInside(p: string, allowed: string[]): Promise<boolean> {
try {
const parent = await fs.realpath(path.dirname(p));
return allowed.some(d => parent === path.resolve(d) || parent.startsWith(path.resolve(d) + path.sep));
} catch { return false; }
} Try / catch
try {
await writeFile({ path, content });
} catch (e) {
if (e instanceof Error && e.message.startsWith('Access denied - parent directory')) {
// parent resolves outside allowlist; write to a path whose parent is inside
}
} Prevention
- Write targets whose parent dir (by realpath) is inside the allowlist.
- Avoid '..' in write paths; normalize first.
- Allowlist the real parent directory for legitimate cross-dir writes.
When it happens
Trigger: Calling a write/create tool (`write_file`, `create_directory`) for a path whose parent (after symlink resolution) is outside the allowlist — e.g. creating `/home/me/projects/../../../tmp/x` where `/tmp` is not allowed, or a parent dir that is itself a symlink escaping the sandbox.
Common situations: Write tools whose target path traverses out via `..`, parent directories that are symlinks to disallowed locations, or operators who allowlist a dir but not the actual realpath parent of a write target.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied - path outside allowed directories: ${absolute
- Access denied - symlink target outside allowed directories:
- Unsupported URL protocol for ${dataUri}. Only http, https, a
- Domain ${domain} is not in the allowed domains list.
- Cannot specify both head and tail parameters simultaneously
AI-assisted analysis of modelcontextprotocol/servers@76d64c822f (2026-08-12).
Data as JSON: /api/errors/ac783cf06e019301.
Report an issue: GitHub.