You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Gemini CLI fails to validate WSL paths on Windows when running in a MinGW environment. All file operations fail with "Path not in workspace" errors, even though the path is clearly within the workspace.
The core issue: When paths are normalized to lowercase on Windows, fs.realpathSync() fails to resolve WSL UNC paths (e.g., \\wsl.localhost\ubuntu\...) because the actual filesystem uses mixed case (e.g., \\wsl.localhost\Ubuntu\...). When realpathSync() fails, it returns a truncated path in the error object, which then fails validation.
For example:
> list all MD files in this repo
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ x FindFiles {"pattern":"**/*.md"} │
│ │
│ Path not in workspace: Attempted path "\\wsl.localhost\Ubuntu\home\yclian\Development\github.com\google-gemini\gemini-cli" resolves outside the allowed workspace directories: │
│ \\wsl.localhost\Ubuntu\home\yclian\Development\github.com\google-gemini\gemini-cli, C:\Users\yclian\Development\github.com\obra\superpowers\skills\using-superpowers, │
│ \\WSL.LOCALHOST\UBUNTU\home\yclian\Development\github.com\google-gemini\gemini-cli\.gemini\skills\docs-writer or the project temp directory: │
│ C:\Users\yclian\.gemini\tmp\9fae3ef839388a51e8c3401e36b0aa881ab6b25d07eecdf9e027b5a5ba9248f2 │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ x Shell {"description":"List all Markdown files in the repository using the find command.","command":"find . -name \"*.md\""} │
│ │
│ Path not in workspace: Attempted path "\\wsl.localhost\Ubuntu\home\yclian\Development\github.com\google-gemini\gemini-cli" resolves outside the allowed workspace directories: │
│ \\wsl.localhost\Ubuntu\home\yclian\Development\github.com\google-gemini\gemini-cli, C:\Users\yclian\Development\github.com\obra\superpowers\skills\using-superpowers, │
│ \\WSL.LOCALHOST\UBUNTU\home\yclian\Development\github.com\google-gemini\gemini-cli\.gemini\skills\docs-writer or the project temp directory: │
│ C:\Users\yclian\.gemini\tmp\9fae3ef839388a51e8c3401e36b0aa881ab6b25d07eecdf9e027b5a5ba9248f2 │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Root cause
The issue is in packages/core/src/utils/workspaceContext.ts, specifically in the isPathWithinRoot() method. On Windows, path.relative() performs case-sensitive string comparison for UNC/WSL paths, even though Windows filesystems are case-insensitive.
When comparing:
\\wsl.localhost\Ubuntu-20.04\... (original case from user input)
\\wsl.localhost\ubuntu-20.04\... (lowercase from stored workspace)
The path.relative() function treats these as different paths and returns ..\..\.., causing the validation to fail.
Breaking commits
This issue was likely introduced by these two commits that added lowercase path normalization on Windows:
Added normalizePath() method in projectRegistry.ts:70-76 that normalizes to lowercase
Both commits normalized paths to lowercase for case-insensitive comparison on Windows, but this breaks WSL paths because fs.realpathSync() requires exact filesystem casing to resolve UNC paths.
The issue involves a chain of problems:
Premature lowercase normalization: Paths are normalized to lowercase (e.g., \\wsl.localhost\ubuntu\...) before filesystem operations
Failed path resolution: fs.realpathSync() fails because WSL paths are case-sensitive for UNC paths - the actual path is \\wsl.localhost\Ubuntu\... (capital U)
Path truncation: When realpathSync() fails with ENOENT, the error object's e.path contains a truncated path (e.g., .../development instead of .../development/github.com/google-gemini/gemini-cli)
Validation failure: The truncated path is then compared against the full workspace path, causing path.relative() to return ..\..\.., which fails the "within workspace" check
Proposed fix
Note: My current workaround uses Option 2 (defensive fixes in workspaceContext.ts) applied to my local installation. I could submit a PR with Option 1 (the proper fix that removes the premature normalization) later this week.
Option 1: Proper fix
Remove the premature lowercase normalization that causes the issue:
1. Fix config.ts (line ~1857)
Remove the lowercase normalization:
constrealpath=(p: string)=>{letresolved: string;try{resolved=fs.realpathSync(p);}catch{resolved=path.resolve(p);}// Don't normalize to lowercase - it breaks WSL pathsreturnresolved;};
2. Fix projectRegistry.ts (line ~70-76)
Remove the lowercase normalization:
privatenormalizePath(projectPath: string): string {// Don't normalize to lowercase on Windows - it breaks WSL pathsreturnpath.resolve(projectPath);}
3. Fix isPathWithinRoot in workspaceContext.ts (line ~207)
Add case-insensitive comparison for Windows:
privateisPathWithinRoot(pathToCheck: string,rootDirectory: string,): boolean {// Normalize both paths to lowercase on Windows for case-insensitive comparisonconstnormalizedRoot=os.platform()==='win32'
? rootDirectory.toLowerCase()
: rootDirectory;constnormalizedPath=os.platform()==='win32'
? pathToCheck.toLowerCase()
: pathToCheck;constrelative=path.relative(normalizedRoot,normalizedPath);return(!relative.startsWith(`..${path.sep}`)&&relative!=='..'&&!path.isAbsolute(relative));}
Important: Normalize to lowercase only for comparison, never for storage or filesystem operations.
Option 2: Defensive fix / Local workaround
This defensive workaround in workspaceContext.ts handles incorrect casing:
1. Fix Constructor (line ~26)
constructor(targetDir: string, additionalDirectories: string[] = []) {
- this.targetDir = targetDir;+ // Ensure targetDir is in original case from filesystem+ try {+ this.targetDir = fs.realpathSync(targetDir);+ } catch {+ this.targetDir = targetDir;+ }
this.addDirectory(targetDir);
this.addDirectories(additionalDirectories);
this.initialDirectories = new Set(this.directories);
}
2. Fix fullyResolvedPath (line ~182)
private fullyResolvedPath(pathToCheck: string): string {
try {
- return fs.realpathSync(path.resolve(this.targetDir, pathToCheck));+ let resolvedInput = path.resolve(this.targetDir, pathToCheck);++ // On Windows, if pathToCheck is already absolute with incorrect casing, fix it+ if (os.platform() === 'win32' && path.isAbsolute(pathToCheck)) {+ try {+ resolvedInput = fs.realpathSync(pathToCheck);+ } catch {+ // Normalize the case by matching against targetDir+ const normalizedPathToCheck = pathToCheck.toLowerCase();+ const normalizedTargetDir = this.targetDir.toLowerCase();++ if (normalizedPathToCheck.startsWith(normalizedTargetDir)) {+ resolvedInput = this.targetDir + pathToCheck.substring(normalizedTargetDir.length); + }+ }+ }++ return fs.realpathSync(resolvedInput);
} catch (e: unknown) {
if (
isNodeError(e) &&
e.code === 'ENOENT' &&
e.path &&
!this.isFileSymlink(e.path)
) {
return e.path;
}
throw e;
}
}
What happened?
Gemini CLI fails to validate WSL paths on Windows when running in a MinGW environment. All file operations fail with "Path not in workspace" errors, even though the path is clearly within the workspace.
The core issue: When paths are normalized to lowercase on Windows,
fs.realpathSync()fails to resolve WSL UNC paths (e.g.,\\wsl.localhost\ubuntu\...) because the actual filesystem uses mixed case (e.g.,\\wsl.localhost\Ubuntu\...). WhenrealpathSync()fails, it returns a truncated path in the error object, which then fails validation.For example:
Root cause
The issue is in
packages/core/src/utils/workspaceContext.ts, specifically in theisPathWithinRoot()method. On Windows,path.relative()performs case-sensitive string comparison for UNC/WSL paths, even though Windows filesystems are case-insensitive.When comparing:
\\wsl.localhost\Ubuntu-20.04\...(original case from user input)\\wsl.localhost\ubuntu-20.04\...(lowercase from stored workspace)The
path.relative()function treats these as different paths and returns..\..\.., causing the validation to fail.Breaking commits
This issue was likely introduced by these two commits that added lowercase path normalization on Windows:
5f569fa10(2026-01-27) - "refactor(core): centralize path validation and allow temp dir access for tools (refactor(core): centralize path validation and allow temp dir access for tools #17185)"toLowerCase()normalization inconfig.ts:18576fb3b0900(2026-02-06) - "Shorten temp directory (Shorten temp directory #17901)"normalizePath()method inprojectRegistry.ts:70-76that normalizes to lowercaseBoth commits normalized paths to lowercase for case-insensitive comparison on Windows, but this breaks WSL paths because
fs.realpathSync()requires exact filesystem casing to resolve UNC paths.The issue involves a chain of problems:
\\wsl.localhost\ubuntu\...) before filesystem operationsfs.realpathSync()fails because WSL paths are case-sensitive for UNC paths - the actual path is\\wsl.localhost\Ubuntu\...(capital U)realpathSync()fails withENOENT, the error object'se.pathcontains a truncated path (e.g.,.../developmentinstead of.../development/github.com/google-gemini/gemini-cli)path.relative()to return..\..\.., which fails the "within workspace" checkProposed fix
Note: My current workaround uses Option 2 (defensive fixes in
workspaceContext.ts) applied to my local installation. I could submit a PR with Option 1 (the proper fix that removes the premature normalization) later this week.Option 1: Proper fix
Remove the premature lowercase normalization that causes the issue:
1. Fix
config.ts(line ~1857)Remove the lowercase normalization:
2. Fix
projectRegistry.ts(line ~70-76)Remove the lowercase normalization:
3. Fix
isPathWithinRootinworkspaceContext.ts(line ~207)Add case-insensitive comparison for Windows:
Important: Normalize to lowercase only for comparison, never for storage or filesystem operations.
Option 2: Defensive fix / Local workaround
This defensive workaround in
workspaceContext.tshandles incorrect casing:1. Fix Constructor (line ~26)
constructor(targetDir: string, additionalDirectories: string[] = []) { - this.targetDir = targetDir; + // Ensure targetDir is in original case from filesystem + try { + this.targetDir = fs.realpathSync(targetDir); + } catch { + this.targetDir = targetDir; + } this.addDirectory(targetDir); this.addDirectories(additionalDirectories); this.initialDirectories = new Set(this.directories); }2. Fix fullyResolvedPath (line ~182)
private fullyResolvedPath(pathToCheck: string): string { try { - return fs.realpathSync(path.resolve(this.targetDir, pathToCheck)); + let resolvedInput = path.resolve(this.targetDir, pathToCheck); + + // On Windows, if pathToCheck is already absolute with incorrect casing, fix it + if (os.platform() === 'win32' && path.isAbsolute(pathToCheck)) { + try { + resolvedInput = fs.realpathSync(pathToCheck); + } catch { + // Normalize the case by matching against targetDir + const normalizedPathToCheck = pathToCheck.toLowerCase(); + const normalizedTargetDir = this.targetDir.toLowerCase(); + + if (normalizedPathToCheck.startsWith(normalizedTargetDir)) { + resolvedInput = this.targetDir + pathToCheck.substring(normalizedTargetDir.length); + } + } + } + + return fs.realpathSync(resolvedInput); } catch (e: unknown) { if ( isNodeError(e) && e.code === 'ENOENT' && e.path && !this.isFileSymlink(e.path) ) { return e.path; } throw e; } }3. Fix isPathWithinRoot (line ~207)
private isPathWithinRoot( pathToCheck: string, rootDirectory: string, ): boolean { - const relative = path.relative(rootDirectory, pathToCheck); + // Normalize both paths to lowercase on Windows for case-insensitive comparison + const normalizedRoot = os.platform() === 'win32' + ? rootDirectory.toLowerCase() + : rootDirectory; + const normalizedPath = os.platform() === 'win32' + ? pathToCheck.toLowerCase() + : pathToCheck; + + const relative = path.relative(normalizedRoot, normalizedPath); return ( !relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative) ); }Note: Add
import * as os from 'node:os';at the top of the file for both options.What did you expect to happen?
Client information
Client Information
Run
geminito enter the interactive CLI, then run the/aboutcommand.Login information
No response
Anything else we need to know?
cc: @NTaylorMullen @joshualitt