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.execute()` carefully resolves symlinks when building internal path sets (lines 208–242), but the **final output handed to the LLM** (line 273) uses `entry.fullpath()` — the raw, symlink-unresolved path from the `glob` library.
This inconsistency means the path strings returned to the model differ from the canonical paths that `read_file`, `edit`, and `write_file` expect after resolving their inputs — causing **"file not found"** or **"path not in workspace"** failures on follow-up tool calls.
**Root cause — two different path forms used in the same function:**
Internal resolution (correct) — lines 208–222:
```ts
let realTargetDir = this.config.getTargetDir();
try { realTargetDir = resolveToRealPath(realTargetDir); } catch { }
const relativePaths = allEntries.map((p) => {
let realFullPath = p.fullpath();
try { realFullPath = resolveToRealPath(realFullPath); } catch { }
return path.relative(realTargetDir, realFullPath); // real ↔ real ✓
});
```
Final output handed to LLM (inconsistent) — lines 272–275:
```ts
const sortedAbsolutePaths = sortedEntries.map((entry) =>
entry.fullpath() // ← raw symlink path, NOT resolved
);
```
The `glob` library v12 documents this explicitly: `fullpath()` does **"only string path resolution — does not make an extra system call to get the realpath"**. So when `cwd` is a symlink, `entry.fullpath()` returns the unresolved path.
**This affects every macOS user** because macOS resolves `/tmp` → `/private/tmp`. Any project opened from `/tmp/myproject` will have `getTargetDir()` = `/tmp/myproject` but tools that call `resolveToRealPath` internally see `/private/tmp/myproject` — the paths from GlobTool never match.
### What did you expect to happen?
The file paths returned to the LLM should be the **canonical, symlink-resolved** absolute paths — consistent with what `read_file`, `edit`, and `write_file` will accept after their own path resolution.
**Suggested fix:**
Build a raw → real path map during the existing `relativePaths` loop (no extra `fs` calls needed) and use it when constructing the final output:
```ts
const rawToReal = new Map();
allEntries.forEach((p) => {
let real = p.fullpath();
try { real = resolveToRealPath(real); } catch { }
rawToReal.set(p.fullpath(), real);
});
// In the output section, replace entry.fullpath() with:
const sortedAbsolutePaths = sortedEntries.map((entry) =>
rawToReal.get(entry.fullpath()) ?? entry.fullpath()
);
```
I am happy to open a PR with this fix and a unit test that creates a symlink workspace and asserts all returned paths are canonical — 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)
glob dependency version: 12.0.0 (per packages/core/package.json)
```
### Login information
Not auth-dependent — reproducible with any login method.
### Anything else we need to know?
**How to reproduce:**
1. Create a symlink workspace:
```sh
mkdir -p /tmp/realproject/src
ln -s /tmp/realproject /tmp/symproject
echo "export const x = 1;" > /tmp/realproject/src/index.ts
```
2. Launch Gemini CLI with the symlinked directory as the target.
3. Ask: *"Find all TypeScript files"*
4. GlobTool runs with `cwd = /tmp/symproject`.
5. Result returned to model: `/tmp/symproject/src/index.ts` (raw path).
6. Model calls `read_file({ absolute_path: "/tmp/symproject/src/index.ts" })`.
7. `read_file` resolves to `/tmp/realproject/src/index.ts` internally → path mismatch → error.
**On macOS this is always active** — no symlink setup needed. `/tmp` is a symlink to `/private/tmp` on every macOS installation.
**Exact lines in `packages/core/src/tools/glob.ts`:**
- Correct internal resolution: lines 208–222
- Inconsistent final output: lines 272–275
- `glob` v12 fullpath() behaviour: confirmed via official npm docs
This is distinct from issue #1121 (symlink security bypass) — that is about path validation escaping the sandbox. This bug is about path format inconsistency between GlobTool output and what other tools accept as input.
Contributor guide
Research direction
Start in packages/core/src/tools/glob.ts at GlobTool.execute(), reading the existing real-path handling around lines 208–242 and output construction around lines 272–275. Reproduce with a symlinked workspace, then add and run a unit test showing returned paths are canonical and usable by read_file, edit, and write_file; done means those tools receive matching paths.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100