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 Beginner friendly
#28,416 6 comments 0 reactions 0 assignees View on GitHub
area/core effort/medium kind/bug priority/p2 status/bot-triaged
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

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.