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

Open
#28,415 6 comments 0 reactions 0 assignees View on GitHub
area/core effort/small kind/bug priority/p2 Stale status/bot-triaged
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.