modelcontextprotocol/servers · error · Error
Access denied - Windows-style path received on a POSIX host:
Error message
Access denied - Windows-style path received on a POSIX host: ${requestedPath} What it means
On POSIX hosts validatePath rejects Windows-style absolute paths (drive letter followed by \ or /, or a bare drive like 'C:'). Accepting them would silently create a literal file named 'C:\\Users\\...' inside the allowed root while reporting success for the wrong location. The error is thrown before any filesystem access.
Source
Thrown at src/filesystem/lib.ts:146
return path.join(currentPath, ...relativeParts.slice(index));
}
currentPath = await fs.realpath(path.join(currentPath, equivalentMatches[0]));
if (!isPathWithinAllowedDirectories(normalizePath(currentPath), allowedDirectories)) {
throw new Error(`Access denied - symlink target outside allowed directories: ${currentPath} not in ${allowedDirectories.join(', ')}`);
}
}
return currentPath;
}
export async function validatePath(requestedPath: string): Promise<string> {
const expandedPath = expandHome(requestedPath);
// Do not silently reinterpret a Windows drive path as a relative POSIX path.
// This would create a literal filename such as `C:\\Users\\...` inside the
// allowed root and report success for the wrong location.
if (process.platform !== 'win32' && /^(?:[A-Za-z]:)(?:[\\/]|$)/.test(expandedPath)) {
throw new Error(`Access denied - Windows-style path received on a POSIX host: ${requestedPath}`);
}
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);View on GitHub (pinned to 579c3903f3)
Solutions
- Convert the path to its POSIX form (e.g. '/mnt/c/Users/me/file.txt' under WSL) before calling the tool
- Use a POSIX absolute or home-relative path like '~/file.txt' that exists on the host
- Fix the caller/config to detect the platform and emit platform-appropriate paths
Example fix
// before (on Linux)
await read_file('C:\\Users\\me\\notes.txt');
// after
await read_file('/mnt/c/Users/me/notes.txt'); // WSL, or a native POSIX path Defensive patterns
Strategy: validation
Validate before calling
const WIN_PATH = /^(?:[A-Za-z]:)(?:[\\/]|$)/;
if (process.platform !== 'win32' && WIN_PATH.test(p)) {
throw new Error(`Refusing Windows-style path on POSIX host: ${p}`);
} Type guard
function isWindowsStylePath(p: string): boolean {
return /^(?:[A-Za-z]:)(?:[\\/]|$)/.test(p);
} // guard callers before invoking the tool Prevention
- Normalize paths to the host platform before calling filesystem tools
- In WSL use /mnt/c/... forms, not C:\\...
- Keep per-platform path constants in config instead of hardcoding
- Lint configs/scripts for drive-letter patterns when deploying to POSIX
When it happens
Trigger: Passing a path like 'C:\\Users\\me\\file.txt' or 'C:/Users/me' to any filesystem tool while the server runs on Linux/macOS — typically from a config or script authored on Windows.
Common situations: Sharing MCP client configs or tool arguments between Windows and POSIX machines; WSL sessions receiving Windows paths from the Windows side; hardcoded paths in CI scripts copied from Windows developers.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Parent directory does not exist: ${parentDir}
- Could not find exact match for edit:\n${edit.oldText}
- Ambiguous Unicode path component: ${requestedPart}
- Access denied - symlink target outside allowed directories:
- Parent directory does not exist: ${path.dirname(absolute)}
AI-assisted analysis of modelcontextprotocol/servers@579c3903f3 (2026-09-01).
Data as JSON: /api/errors/dd6bc7531e5da2dc.
Report an issue: GitHub.