angular/angular-cli · error · Error
Access denied: path '${path}' is outside allowed roots.
Error message
Access denied: path '${path}' is outside allowed roots. What it means
The Angular CLI MCP server sandbox every filesystem access (stat, existsSync, readFile, glob) through checkPath, which resolves symlinks and requires the real path to fall under one of the configured roots. This error is thrown when the requested path, after canonicalization, is not inside any allowed root, preventing the MCP server from reading files outside the workspace.
Source
Thrown at packages/angular/cli/src/commands/mcp/host.ts:339
// Reached filesystem root
throw err;
}
current = parent;
}
}
} else {
throw e;
}
}
const isAllowed = roots.some((root) => {
const rel = relative(root, realPath);
return !rel.startsWith('..') && !isAbsolute(rel);
});
if (!isAllowed) {
throw new Error(`Access denied: path '${path}' is outside allowed roots.`);
}
}
return {
...baseHost,
setRoots(newRoots: string[]) {
roots = newRoots.length > 0 ? resolveRoots(newRoots) : defaultRoots;
},
stat(path: string) {
checkPath(path);
return baseHost.stat(path);
},
existsSync(path: string) {
checkPath(path);
return baseHost.existsSync(path);
},View on GitHub (pinned to bb72145f9a)
Solutions
- Move the requested path inside one of the allowed roots (workspace directory) and retry.
- Have the MCP client call setRoots with the directory containing the desired path before accessing it.
- Resolve or remove symlinks that point outside the allowed roots.
- Use MCP tools like list_projects to discover workspaces that are actually accessible.
Example fix
// before
await host.readFile('/etc/hosts', 'utf8');
// after
host.setRoots(['/home/dev/my-app']);
await host.readFile('/home/dev/my-app/src/main.ts', 'utf8'); Defensive patterns
Strategy: validation
Validate before calling
import { relative, isAbsolute, resolve, realpathSync } from 'node:path';
function isInsideRoots(path: string, roots: string[]): boolean {
const realPath = realpathSync(resolve(path));
return roots.some((root) => {
const rel = relative(resolve(root), realPath);
return !rel.startsWith('..') && !isAbsolute(rel);
});
}
if (!isInsideRoots(targetPath, allowedRoots)) throw new Error('path outside roots'); Type guard
function isWithinRoot(p: string, roots: string[]): p is string {
const rel = relative(resolve(roots[0] ?? '/'), resolve(p));
return !rel.startsWith('..') && !isAbsolute(rel);
} Try / catch
try {
await host.readFile(path, 'utf8');
} catch (e) {
if ((e as Error).message.includes('is outside allowed roots')) {
host.setRoots([resolve(path).split('/').slice(0, 3).join('/')]);
await host.readFile(path, 'utf8');
} else throw e;
} Prevention
- Always pass workspace-relative or verified workspace-absolute paths to MCP host operations
- Call setRoots when switching workspaces before any file access
- Resolve symlinks before passing paths, since checkPath canonicalizes via realpath
- Avoid '..' segments in paths; build paths from the workspace root instead
When it happens
Trigger: Calling any MCP host operation (stat/existsSync/readFile/glob/executeNgCommand/startNgProcess) with an absolute path outside the roots set via setRoots, a path containing '..' segments that resolves outside the roots, or a symlink pointing outside the allowed roots.
Common situations: MCP clients hardcoding absolute paths to files in another project; referring to a shared directory outside the workspace; symlinked node_modules or home-directory configs resolving outside roots; stale roots after the user switched workspaces without calling setRoots.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied: glob pattern '${pattern}' contains path trave
- Workspace path is outside the allowed MCP roots: ${workspace
- Failed to access path: ${fileOrDirPath}
- No angular.json found at ${workspacePathInput}. You can use
- Workspace path is outside the allowed MCP roots: ${workspace
AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30).
Data as JSON: /api/errors/77972a5f7f48c5f6.
Report an issue: GitHub.