google-gemini/gemini-cli · error · Error

Path validation failed: ${pathError}

Error message

Path validation failed: ${pathError}

What it means

Thrown by Task.getProposedContent (used by the edit/replace tool) when config.validatePathAccess(resolvedPath, 'read') returns a non-null error string. validatePathAccess is the sandbox/path-traversal guard from gemini-cli-core's PathValidator: it rejects paths outside the allowed root, forbidden extensions, symlink escapes, and permission failures. The resolved path is computed by path.resolve(targetDir, file_path) so relative file_path values are bound to the workspace target dir.

Source

Thrown at packages/a2a-server/src/agent/task.ts:667

      kind: 'message',
      role: 'agent',
      parts: messageParts,
      messageId: uuidv4(),
      taskId,
      contextId,
    };
  }

  private async getProposedContent(
    file_path: string,
    old_string: string,
    new_string: string,
  ): Promise<string> {
    // Validate path to prevent path traversal vulnerabilities
    const resolvedPath = path.resolve(this.config.getTargetDir(), file_path);
    const pathError = this.config.validatePathAccess(resolvedPath, 'read');
    if (pathError) {
      throw new Error(`Path validation failed: ${pathError}`);
    }

    try {
      const rawContent = await fs.readFile(resolvedPath, 'utf8');
      const hasCrlf = rawContent.includes('\r\n');
      const currentContent = rawContent.replace(/\r\n/g, '\n');
      const normalizedOldString = old_string.replace(/\r\n/g, '\n');
      const normalizedNewString = new_string.replace(/\r\n/g, '\n');
      const proposedContent = this._applyReplacement(
        currentContent,
        normalizedOldString,
        normalizedNewString,
        normalizedOldString === '' && currentContent === '',
      );
      return hasCrlf ? proposedContent.replace(/\n/g, '\r\n') : proposedContent;
    } catch (err) {
      if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
      return '';

View on GitHub (pinned to 5024443c72)

Solutions

  1. Read the interpolated pathError - it states the specific reason (outside root, forbidden extension, permission).
  2. If the path is legitimately within scope, check that config.getTargetDir() and the sandbox root/extension allowlist match the workspace you intend.
  3. Avoid passing absolute paths; use paths relative to the workspace root.
  4. If a symlink is involved, confirm the PathValidator extension config permits following it.

Example fix

// before
const resolvedPath = path.resolve(this.config.getTargetDir(), file_path);
const pathError = this.config.validatePathAccess(resolvedPath, 'read');
if (pathError) throw new Error(`Path validation failed: ${pathError}`);

// after (caller-side: pre-validate and surface a tool error instead of throwing)
const pathError = this.config.validatePathAccess(resolvedPath, 'read');
if (pathError) {
  return { status: 'failed', error: `Path not allowed: ${pathError}` };
}
Defensive patterns

Strategy: validation

Validate before calling

import path from 'node:path';

function assertEditPathSafe(config: { getTargetDir(): string; validatePathAccess(p: string, mode: 'read'|'write'): string | null }, filePath: string) {
  const resolved = path.resolve(config.getTargetDir(), filePath);
  const err = config.validatePathAccess(resolved, 'read');
  if (err) throw new Error(`Refusing edit: ${err}`);
}
// call before constructing the edit tool call

Try / catch

try {
  return await task.getProposedContent(file_path, old_string, new_string);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Path validation failed: ')) {
    return { status: 'failed', error: e.message } as const;
  }
  throw e;
}

Prevention

When it happens

Trigger: The edit tool is invoked with a file_path that, after resolution, falls outside the workspace sandbox; the path points at a forbidden extension or a symlink that escapes the allowed root; the underlying directory lacks read permission. validatePathAccess returns a reason string which is interpolated into the message.

Common situations: Agent emits an edit with an absolute path (/etc/hosts) or a traversal (../../secret); workspace target dir misconfigured so legitimate files appear outside the root; sandbox extension allowlist excludes the file type; symlink inside the workspace points outside.

Related errors


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