What happened?
GlobTool.validateToolParamValues checks exactly one directory (config.getTargetDir()) but GlobToolInvocation.execute() searches all directories returned by workspaceContext.getDirectories().
When those sets differ (multi-root workspaces via includeDirectories), the extra workspace directories are never validated. If any of them is stale, deleted, or inaccessible, glob silently returns zero entries for that root — no error is surfaced to the model or the user. The result is either a silent partial match or a false "No files found" response.
Root cause — two separate code paths with mismatched scope:
validateToolParamValues (lines 333–374 of packages/core/src/tools/glob.ts):
searchDirAbsolute = resolveToRealPath(
path.resolve(this.config.getTargetDir(), params.dir_path || '.'),
);
When dir_path is absent this always resolves to getTargetDir() — one directory. The fs.existsSync / fs.statSync checks only apply to that one directory.
execute() (lines 176–179):
} else {
// Search across all workspace directories
searchDirectories = workspaceDirectories;
}
workspaceDirectories can contain many roots. None of the extra roots were validated. Glob failures on them are swallowed silently.
Secondary — dead code on line 353:
const targetDir = searchDirAbsolute || this.config.getTargetDir();
searchDirAbsolute is always assigned before this line (or the function returned early), so the fallback branch is unreachable. This indicates the validation and execution paths have drifted apart.
What did you expect to happen?
validateToolParamValues should validate the same set of directories that execute() will actually search.
When dir_path is absent, it should iterate workspaceContext.getDirectories() and validate each one — mirroring exactly what execute() does. If any workspace directory is inaccessible, an error should be returned before execution begins, rather than silently producing partial or empty results.
Suggested fix:
protected override validateToolParamValues(params: GlobToolParams): string | null {
const dirsToValidate = params.dir_path
? [path.resolve(this.config.getTargetDir(), params.dir_path)]
: this.config.getWorkspaceContext().getDirectories();
for (const dir of dirsToValidate) {
let resolved: string;
try {
resolved = resolveToRealPath(dir);
} catch (err) {
return err instanceof Error ? err.message : String(err);
}
const accessError = this.config.validatePathAccess(resolved, 'read');
if (accessError) return accessError;
if (!fs.existsSync(resolved)) return `Search path does not exist: ${resolved}`;
if (!fs.statSync(resolved).isDirectory()) return `Search path is not a directory: ${resolved}`;
}
if (!params.pattern || params.pattern.trim() === '') {
return "The 'pattern' parameter cannot be empty.";
}
return null;
}
I am happy to open a PR with this fix and a unit test covering multi-root workspace validation once the approach is confirmed — please assign me.
Client information
Client Information
Run gemini to enter the interactive CLI, then run the /about command.
> /about
Reproduced from source zip (main branch, nightly 0.51.0-nightly.20260625.g3fbf93e26)
Platform: Linux (Ubuntu 24.04)
Node.js: v20.x (required minimum per gemini-cli package.json)
Platform: Linux / macOS (any OS with multi-root includeDirectories workspace)
Login information
Not auth-dependent — reproducible with any login method.
Anything else we need to know?
How to reproduce:
- Configure two
includeDirectories roots in ~/.gemini/settings.json:
{ "includeDirectories": ["/valid/project", "/tmp/stale-dir"] }
- Ensure
/tmp/stale-dir does not exist on disk.
- Launch Gemini CLI from
/valid/project.
- Ask the model to find files: "find all *.ts files"
GlobTool is invoked without dir_path → validateToolParamValues passes (only checks /valid/project).
execute() attempts to iterate both dirs → silent empty result for /tmp/stale-dir.
Result: Files from /valid/project may be missing from results with no error shown.
Exact lines in packages/core/src/tools/glob.ts:
- Validation scope (too narrow): lines 333–374
- Execution scope (all workspace dirs): lines 139–179
- Dead code fallback: line 353
This is distinct from existing issues #1118 (feature request for multi-dir support) and #12565 (symlink traversal) — those concern different behaviour.
What happened?
GlobTool.validateToolParamValueschecks exactly one directory (config.getTargetDir()) butGlobToolInvocation.execute()searches all directories returned byworkspaceContext.getDirectories().When those sets differ (multi-root workspaces via
includeDirectories), the extra workspace directories are never validated. If any of them is stale, deleted, or inaccessible, glob silently returns zero entries for that root — no error is surfaced to the model or the user. The result is either a silent partial match or a false "No files found" response.Root cause — two separate code paths with mismatched scope:
validateToolParamValues(lines 333–374 ofpackages/core/src/tools/glob.ts):When
dir_pathis absent this always resolves togetTargetDir()— one directory. Thefs.existsSync/fs.statSyncchecks only apply to that one directory.execute()(lines 176–179):workspaceDirectoriescan contain many roots. None of the extra roots were validated. Glob failures on them are swallowed silently.Secondary — dead code on line 353:
searchDirAbsoluteis always assigned before this line (or the function returned early), so the fallback branch is unreachable. This indicates the validation and execution paths have drifted apart.What did you expect to happen?
validateToolParamValuesshould validate the same set of directories thatexecute()will actually search.When
dir_pathis absent, it should iterateworkspaceContext.getDirectories()and validate each one — mirroring exactly whatexecute()does. If any workspace directory is inaccessible, an error should be returned before execution begins, rather than silently producing partial or empty results.Suggested fix:
I am happy to open a PR with this fix and a unit test covering multi-root workspace validation once the approach is confirmed — please assign me.
Client information
Client Information
Run
geminito enter the interactive CLI, then run the/aboutcommand.Login information
Not auth-dependent — reproducible with any login method.
Anything else we need to know?
How to reproduce:
includeDirectoriesroots in~/.gemini/settings.json:{ "includeDirectories": ["/valid/project", "/tmp/stale-dir"] }/tmp/stale-dirdoes not exist on disk./valid/project.GlobToolis invoked withoutdir_path→validateToolParamValuespasses (only checks/valid/project).execute()attempts to iterate both dirs → silent empty result for/tmp/stale-dir.Result: Files from
/valid/projectmay be missing from results with no error shown.Exact lines in
packages/core/src/tools/glob.ts:This is distinct from existing issues #1118 (feature request for multi-dir support) and #12565 (symlink traversal) — those concern different behaviour.