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
- Read the interpolated pathError - it states the specific reason (outside root, forbidden extension, permission).
- If the path is legitimately within scope, check that config.getTargetDir() and the sandbox root/extension allowlist match the workspace you intend.
- Avoid passing absolute paths; use paths relative to the workspace root.
- 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
- Always pass file_path relative to the workspace root, never absolute paths from arbitrary user input.
- Keep the PathValidator extension allowlist and sandbox root in sync with config.getTargetDir().
- Reject edit tool calls whose path contains '..' at the dispatch layer before they reach getProposedContent.
- Audit symlinks inside the workspace - dereferenced targets must remain under the allowed root.
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
- Workspace path ${resolvedPath} is outside the allowed root d
- Security violation: The path "${trimmedPath}" is outside the
- Invalid taskId: ${taskId}
- Security violation: Null byte detected in path.
- Invalid path: Directory traversal not allowed.
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/875968daf067322e.
Report an issue: GitHub.