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:
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:
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:
const rawToReal = new Map<string, string>();
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.
> /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:
- Create a symlink workspace:
mkdir -p /tmp/realproject/src
ln -s /tmp/realproject /tmp/symproject
echo "export const x = 1;" > /tmp/realproject/src/index.ts
- Launch Gemini CLI with the symlinked directory as the target.
- Ask: "Find all TypeScript files"
- GlobTool runs with
cwd = /tmp/symproject.
- Result returned to model:
/tmp/symproject/src/index.ts (raw path).
- Model calls
read_file({ absolute_path: "/tmp/symproject/src/index.ts" }).
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.
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) usesentry.fullpath()— the raw, symlink-unresolved path from thegloblibrary.This inconsistency means the path strings returned to the model differ from the canonical paths that
read_file,edit, andwrite_fileexpect 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:
Final output handed to LLM (inconsistent) — lines 272–275:
The
globlibrary v12 documents this explicitly:fullpath()does "only string path resolution — does not make an extra system call to get the realpath". So whencwdis a symlink,entry.fullpath()returns the unresolved path.This affects every macOS user because macOS resolves
/tmp→/private/tmp. Any project opened from/tmp/myprojectwill havegetTargetDir()=/tmp/myprojectbut tools that callresolveToRealPathinternally 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, andwrite_filewill accept after their own path resolution.Suggested fix:
Build a raw → real path map during the existing
relativePathsloop (no extrafscalls needed) and use it when constructing the final output: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
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:
cwd = /tmp/symproject./tmp/symproject/src/index.ts(raw path).read_file({ absolute_path: "/tmp/symproject/src/index.ts" }).read_fileresolves to/tmp/realproject/src/index.tsinternally → path mismatch → error.On macOS this is always active — no symlink setup needed.
/tmpis a symlink to/private/tmpon every macOS installation.Exact lines in
packages/core/src/tools/glob.ts:globv12 fullpath() behaviour: confirmed via official npm docsThis 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.