google-gemini/gemini-cli · error · UnauthorizedAccessException
Access to forbidden path is denied: {path}
Error message
Access to forbidden path is denied: {path} What it means
This UnauthorizedAccessException is thrown by GeminiSandbox.CheckForbidden() when a sandboxed internal file command (__read or __write) targets a path that matches or sits underneath an entry in the forbiddenPaths set. Forbidden paths are loaded from a forbidden-manifest file and normalized to long 8.3 form before comparison. It is a security guard preventing the low-integrity sandboxed process from touching protected locations.
Source
Thrown at packages/core/src/sandbox/windows/GeminiSandbox.cs:521
RevertToSelf();
}
}
private static string GetNormalizedPath(string path) {
string fullPath = Path.GetFullPath(path);
StringBuilder longPath = new StringBuilder(1024);
uint result = GetLongPathName(fullPath, longPath, (uint)longPath.Capacity);
if (result > 0 && result < longPath.Capacity) {
return longPath.ToString();
}
return fullPath;
}
private static void CheckForbidden(string path, HashSet<string> forbiddenPaths) {
string fullPath = GetNormalizedPath(path);
foreach (string forbidden in forbiddenPaths) {
if (fullPath.Equals(forbidden, StringComparison.OrdinalIgnoreCase) || fullPath.StartsWith(forbidden + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) {
throw new UnauthorizedAccessException("Access to forbidden path is denied: " + path);
}
}
}
private static string QuoteArgument(string arg) {
if (string.IsNullOrEmpty(arg)) return "\"\"";
bool needsQuotes = false;
foreach (char c in arg) {
if (char.IsWhiteSpace(c) || c == '\"') {
needsQuotes = true;
break;
}
}
if (!needsQuotes) return arg;
StringBuilder sb = new StringBuilder();View on GitHub (pinned to 5024443c72)
Solutions
- Identify which forbidden-manifest entry the target path matches and remove/adjust that entry if access is legitimately required.
- Rewrite the sandboxed command to target a path outside the forbidden subtree (e.g., a workspace scratch dir).
- If using __read/__write, verify the path is within the allowed working directory and not under a system or credential path.
- Audit the --forbidden-manifest passed to GeminiSandbox.exe to ensure entries are scoped narrowly (leaf dirs, not over-broad parents).
- Normalize the target path the same way (GetFullPath + GetLongPathName) before issuing the command to predict the match.
Example fix
// before: forbidden-manifest forbids C:\Repo\.git, agent tries to read it GeminiSandbox.exe __read C:\Repo\.git\config // after: read config from an allowed copy outside the forbidden path GeminiSandbox.exe __read C:\Workspace\.gitconfig-copy
Defensive patterns
Strategy: validation
Validate before calling
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
// Replicate GeminiSandbox normalization to pre-check a path before calling __read/__write:
static bool IsForbidden(string path, string[] forbidden) {
string full = Path.GetFullPath(path);
var sb = new StringBuilder(1024);
// P/Invoke GetLongPathName as in GeminiSandbox.cs:507-515
foreach (string f in forbidden) {
if (full.Equals(f, System.StringComparison.OrdinalIgnoreCase) ||
full.StartsWith(f + Path.DirectorySeparatorChar, System.StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
} Try / catch
try {
// __read or __write against 'path'
} catch (UnauthorizedAccessException ex) when (ex.Message.Contains("forbidden path")) {
Console.Error.WriteLine($"Blocked by sandbox policy: {path}. Use an allowed workspace path.");
// route the operation to an allowed directory instead
} Prevention
- Scope --forbidden-manifest entries to specific sensitive directories, not overly broad parents.
- Pre-check target paths against the forbidden set using the same normalization before issuing __read/__write.
- Keep sandboxed file operations inside a dedicated workspace scratch directory.
- Beware symlinks/junctions that resolve under a forbidden subtree; resolve them before checking.
When it happens
Trigger: The sandbox receives an internal __read or __write command (args at Main:310-355) whose target path, after Path.GetFullPath + GetLongPathName normalization, equals a forbidden entry case-insensitively or starts with '<forbidden>\'. For example, forbidden list contains 'C:\Windows\System32' and the command targets 'C:\Windows\System32\drivers\etc\hosts'.
Common situations: An agent or tool inside the sandbox attempts to read a credential file, system directory, or the repo's .git directory that was explicitly forbidden via the --forbidden-manifest. A path-traversal or relative path ('..\..\forbidden\file') resolves into a forbidden subtree after normalization. The manifest was over-broad, forbidding a parent directory that the workload legitimately needs. A symlink or junction resolves under a forbidden path.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Path validation failed: ${pathError}
- Workspace path ${resolvedPath} is outside the allowed root d
- Security violation: The path "${trimmedPath}" is outside the
- Missing mount path '${from}' listed in SANDBOX_MOUNTS
- Failed to mount workspace into LXC container '${containerNam
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/65fce6e12e3c8446.
Report an issue: GitHub.