One evening I was about to hand a settings file to Gemini CLI from one of my own projects. I typed @config and stopped. As an indie developer I keep two config.json files in that repository, one for the app and one for the server, and I could not say which of them would go in.
That reminded me of a GitHub issue I had read a few days earlier. It says that when an @ reference does not resolve directly, the CLI falls back to a glob and uses only the first line of its output. Below I lay out what the report says and the check I now run in folders full of same-name files.
I should say up front that I have not reproduced this behavior myself. Everything under "What the report says" is a summary of the issue, nothing more.
What the report says
Issue #29318 points at the fallback in atCommandProcessor.ts:
The glob output is split by newline and a fixed line index is read, which is fragile. Even when several files match, only the first one is used. The fallback path skips the workspace-trust check. With several workspace roots, the roots are searched one after another instead of in parallel.
The reproduction is short: type an ambiguous reference such as @config, and the first of two or more matches is chosen without asking. The expected behavior listed is to ask the user to disambiguate.
The report names commit 9c1b0a610 on main and gives no version or OS. The only comment is someone offering to take the fix, and no workaround is mentioned. So what I can say for certain is limited to "this has been reported."
Why a silent pick hurts
Nothing errors. The model answers from whatever file arrived, and the answer reads fine. If server/config.json goes in when you meant app/config.json, a reply like "this configuration looks fine" is a verdict on the wrong file. You find out when the real device behaves differently.
The check: count before you type
I cannot change the CLI, so I changed my habit. Before writing a short name after @, I count how many paths it matches. One match, go ahead. Two or more, write the full path.
This script lists files and directories whose names contain a fragment, skipping folders like node_modules:
#!/usr/bin/env python3
"""Count how many files an @name would match before you type it."""
import fnmatch
import os
import sys
SKIP = {".git", "node_modules", ".next", "build", "dist"}
def find_matches(root, needle):
hits = []
for base, dirs, files in os.walk(root):
dirs[:] = [d for d in dirs if d not in SKIP]
for name in files + dirs:
if fnmatch.fnmatch(name.lower(), f"*{needle.lower()}*"):
hits.append(os.path.relpath(os.path.join(base, name), root))
return sorted(hits)
if __name__ == "__main__":
needle = sys.argv[1]
root = sys.argv[2] if len(sys.argv) > 2 else "."
hits = find_matches(root, needle)
for h in hits:
print(h)
if len(hits) > 1:
print(f"ambiguous: {len(hits)} matches. Write the full path after @", file=sys.stderr)
sys.exit(1)
sys.exit(0 if hits else 2)I walk files and directories together because ls and a quick glance both miss a directory that shares a name with a file. The exit codes are separate (1 for ambiguous, 2 for nothing found) so a shell wrapper can tell the two apart.
Running it on a small tree
I built a throwaway tree with app/config.json, server/config.json, app/config/dev.yaml and node_modules/x/config.json, then ran the script.
| Argument | Output | Exit code |
|---|---|---|
config | app/config, app/config.json, server/config.json | 1 (ambiguous) |
dev.yaml | app/config/dev.yaml | 0 |
The node_modules copy stays out, and the directory app/config is counted. So @config there had at least three plausible readings.
The lines I drew
First, I write the full path after @ whenever I can. A short name is fine only after I have confirmed a single match.
Second, I ask the model to mention something specific from the attached file at the start of its answer, so a mix-up shows early.
Third, when same-name files keep piling up, I rename them or exclude some with .geminiignore.
That last one is partly a workaround and partly housekeeping. If the repository has five files with the same name, people mix them up too.
For related habits, see grepping a known-bug table before loading context and auditing approval policies that never matched.
Clues that the wrong file went in
Even with the habit, there are days I type a short name anyway. I have a few clues for catching it afterwards.
If the answer mentions keys or function names I have not seen in the file I meant, I suspect the attachment. The reverse is just as telling: nothing at all about a setting I know is there. When a reply says "the configuration is fine" without touching a single concrete value, I ask what was actually read.
The follow-up can be tiny. "Tell me the path of the file you just read, in one line" is usually enough to expose a mix-up. Early on I skipped that check and worked for hours on the wrong assumption before backing out. Now I ask once, right after attaching a settings file.
If a fix lands
The issue proposes using the structured glob API, asking when several files match, and applying the trust check. If that ships, part of this check becomes unnecessary. Even so, knowing how many same-name files you have is cheap and keeps paying off.
One thing to try today: take the names you type most often (config, index, README) and pass each to the script once. The counts will show you where a bare name is safe after @ and where it is not.
A bare name only when I know there is exactly one. I intend to keep that rule whatever the CLI does next.