google-gemini/gemini-cli · critical

Security violation: The path "${trimmedPath}" is outside the

Error message

Security violation: The path "${trimmedPath}" is outside the allowed root directory.

What it means

After resolving both the allowed root and the workspace path to their canonical (real, symlink-resolved) filesystem paths, the function checks containment. If the workspace path is neither identical to the root nor a strict subdirectory of it, it throws to prevent access outside the sandboxed root.

Source

Thrown at packages/a2a-server/src/utils/path_utils.ts:45

  if (trimmedPath.includes('\0')) {
    throw new Error('Security violation: Null byte detected in path.');
  }

  try {
    const canonicalAllowedRoot = resolveToRealPath(allowedRoot);
    const resolvedWorkspacePath = path.resolve(
      canonicalAllowedRoot,
      trimmedPath,
    );
    const canonicalWorkspacePath = resolveToRealPath(resolvedWorkspacePath);

    // Check if the resolved path is within the allowed root directory
    if (
      canonicalWorkspacePath !== canonicalAllowedRoot &&
      !isSubpath(canonicalAllowedRoot, canonicalWorkspacePath)
    ) {
      throw new Error(
        `Security violation: The path "${trimmedPath}" is outside the allowed root directory.`,
      );
    }

    const stats = await fs.promises.stat(canonicalWorkspacePath);
    if (!stats.isDirectory()) {
      throw new Error(`The path "${trimmedPath}" is not a directory.`);
    }

    return canonicalWorkspacePath;
  } catch (e) {
    if (e instanceof Error && 'code' in e && e.code === 'ENOENT') {
      throw new Error(`The path "${trimmedPath}" does not exist.`);
    }
    throw e; // Re-throw other errors
  }
}

View on GitHub (pinned to 5024443c72)

Solutions

  1. Ensure the workspace path is relative and stays within the root without ../ escapes.
  2. Resolve symlinks before calling the function to understand the real filesystem target.
  3. Only use paths returned by trusted discovery mechanisms within the workspace.
  4. Verify that process.cwd() (the default allowedRoot) is set to the intended sandbox root.
Defensive patterns

Strategy: validation

Validate before calling

import { resolveToRealPath, isSubpath } from '@google/gemini-cli-core';
import * as path from 'node:path';

function isWithinRoot(candidate: string, root: string): boolean {
  const canonicalRoot = resolveToRealPath(root);
  const canonicalCandidate = resolveToRealPath(path.resolve(canonicalRoot, candidate));
  return canonicalCandidate === canonicalRoot || isSubpath(canonicalRoot, canonicalCandidate);
}

// Before calling validateWorkspacePath:
if (!isWithinRoot(userPath, allowedRoot)) {
  throw new Error('Path escapes the allowed root');
}

Prevention

When it happens

Trigger: Passing a relative path with ../ sequences that escape the root (e.g., validateWorkspacePath('../../etc', '/home/user/project')), or an absolute path pointing outside the root, or a path that resolves through a symlink to a location outside the allowed root.

Common situations: Symlinks within the workspace that point outside it; relative paths with excessive parent directory traversals; user-supplied paths not validated upstream; incorrect allowedRoot default (process.cwd() is not what was expected).

Related errors


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