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

  1. Identify which forbidden-manifest entry the target path matches and remove/adjust that entry if access is legitimately required.
  2. Rewrite the sandboxed command to target a path outside the forbidden subtree (e.g., a workspace scratch dir).
  3. If using __read/__write, verify the path is within the allowed working directory and not under a system or credential path.
  4. Audit the --forbidden-manifest passed to GeminiSandbox.exe to ensure entries are scoped narrowly (leaf dirs, not over-broad parents).
  5. 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

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

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/65fce6e12e3c8446. Report an issue: GitHub.