google-gemini / google-gemini/gemini-cli
[Bug] GlobTool returns raw symlink paths in results but uses resolved real paths internally — causes downstream read_file/edit failures on macOS and symlinked workspaces
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
### 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`):
```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):
```ts
} 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:**
```ts
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:**
```ts
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.
```console
> /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:**
1. Configure two `includeDirectories` roots in `~/.gemini/settings.json`:
```json
{ "includeDirectories": ["/valid/project", "/tmp/stale-dir"] }
```
2. Ensure `/tmp/stale-dir` does not exist on disk.
3. Launch Gemini CLI from `/valid/project`.
4. Ask the model to find files: *"find all \*.ts files"*
5. `GlobTool` is invoked without `dir_path` → `validateToolParamValues` passes (only checks `/valid/project`).
6. `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.
Contributor guide
Research direction
Read packages/core/src/tools/glob.ts, focusing first on validateToolParamValues and execute(), and trace workspaceContext.getDirectories(). Add the unit test mentioned in the issue for a multi-root workspace with an inaccessible directory. Done means every directory execute() searches is validated first and access or path errors are surfaced instead of producing partial results.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100