Buried in the Antigravity CLI 1.1.13 notes is a line saying that /codesearch falls back to local search when the bundled ripgrep binary cannot be executed.
That line stopped me.
The fallback is silent. No error, no warning, and results still come back. Which means you can keep using a slower search path indefinitely without ever learning that you switched to it.
I did not want to keep using it blind, so I measured the search paths themselves on a repository I actually run, and worked out how to tell which side of the fallback a machine is on.
One caveat up front: what I measured is not the CLI's internals. It is the search engines the fallback moves between.
As an indie developer moving between app and site repositories all day, a few hundred milliseconds per search adds up dozens of times over. It is the kind of difference worth making visible.
What changed in 1.1.13
Four search-related changes are documented in the 1.1.13 release (August 14, 2026).
| Change | Detail |
|---|---|
| Extraction path | The bundled ripgrep binary now unpacks into the user cache directory instead of /tmp |
| Integrity check | The extracted binary is verified with SHA-256 |
| Concurrency | An atomic rename avoids collisions between simultaneous runs |
/codesearch resilience | Falls back to local search when ripgrep cannot be executed |
If you have run into this in practice, the move away from /tmp makes immediate sense. Plenty of machines mount /tmp with noexec, which means a binary can be written there and still refuse to run. Shared boxes and hardened containers do this routinely.
The reasons a binary fails to execute usually fall into three buckets:
/tmpor the cache directory is mountednoexec- An endpoint security product blocks unsigned binaries from executing
- The cache directory is not writable
The 1.1.13 change relaxes the first one. The other two survive it, because they live outside the CLI.
The exit code tells you which path you are on
"Cannot execute" shows up as shell exit code 126. You can reproduce it locally in four lines:
cp "$(command -v rg)" "$HOME/rgtest"
chmod -x "$HOME/rgtest"
"$HOME/rgtest" --version
echo "exit=$?"On my Linux box:
bash: /home/user/rgtest: Permission denied
exit=126
126 means the file was found but could not be run. Both noexec mounts and security-product blocks land here, and it is distinct from 127, which means the file was not found at all.
One trap worth naming. Do not pipe the command you are testing into head or tail. $? then reports the exit status of the last stage of the pipe, and a 126 turns into a 0. I fell for this once during these measurements and briefly concluded the binary was running fine.
Three things are worth checking on a suspect machine:
| What to check | Command | What you want |
|---|---|---|
Mount options for /tmp | findmnt -no OPTIONS /tmp | No noexec |
| Mount options for the cache directory | findmnt -no OPTIONS "${XDG_CACHE_HOME:-$HOME/.cache}" | Same, and this is the one that matters since 1.1.13 |
| Whether the binary runs | The chmod test above | Exit code other than 126 |
The exact extraction path varies by environment. To find it:
find "${XDG_CACHE_HOME:-$HOME/.cache}" -type f -name 'rg*' 2>/dev/nullMeasuring three search paths on the same repository
The test subject is a Next.js site repository I run in production: 2,225 files and 49 MB excluding .git, mostly MDX and plain text.
Three paths were compared:
ripgrep13.0.0 (rg -n)- GNU grep (
grep -rn --exclude-dir=.git) - A deliberately plain Python implementation, standing in for an unoptimized local search
The third one is not the actual fallback implementation. It is a reference point for "a search with no special optimizations":
import os, re, sys, time
root, pat = sys.argv[1], sys.argv[2]
rx = re.compile(pat)
hits = 0
t0 = time.perf_counter()
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d != ".git"]
for fn in filenames:
path = os.path.join(dirpath, fn)
try:
with open(path, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
if rx.search(line):
hits += 1
except (OSError, UnicodeError):
continue
print(f"{hits} hits {(time.perf_counter() - t0) * 1000:.0f} ms")It walks the tree and runs a regex against each line, excluding only .git, to stay close to the minimum any fallback would plausibly implement.
Each query ran five times; the numbers below are medians with a warm page cache.
| Path | Literal search | Regex search |
|---|---|---|
| ripgrep | 18 ms | 19 ms |
| grep -rn | 32 ms | 39 ms |
| Plain Python scan | 277 ms | 281 ms |
At this size even the slowest path finishes in a third of a second. It feels like a slight pause, not like something worth investigating.
Scale is where it starts to matter. I duplicated the content six times into a 200 MB, 12,654-file corpus and measured again. Since it is duplicated content, it lacks the variety of a real monorepo — read these as a signal about how size behaves, not as a benchmark of your codebase.
| Path | 49 MB / 2,225 files | 200 MB / 12,654 files | Growth |
|---|---|---|---|
| ripgrep | 18 ms | 56 ms | 3.1x |
| grep -rn | 32 ms | 141 ms | 4.4x |
| Plain Python scan | 277 ms | 1,510 ms | 5.5x |
Roughly four times the input, and ripgrep grew 3.1x while the plain scan grew 5.5x. The gap between them widened from 15x to 27x.
So the difference you did not notice on a small repository is exactly the difference you will notice on a project carrying node_modules — and even more so if your agent fires several searches per turn.
The change in coverage matters more than the change in speed
Something else showed up mid-measurement that I find more consequential than the timings: the two paths do not walk the same set of files.
rg --files . | wc -l # 2213
find . -path ./.git -prune -o -type f -print | wc -l # 2225Twelve files apart. Listing them gave .gitignore, .npmrc, and a .gitkeep in each content category. ripgrep skips hidden files by default.
In this repository none of those twelve contained the search term, so all three paths reported the same 9,714 matching lines. No divergence at all.
But that was luck of the draw. ripgrep also honors .gitignore by default, and a project with node_modules or build output will produce very different result volumes depending on whether the fallback applies the same exclusions. There is no guarantee that it does.
A speed difference costs you a wait. A coverage difference changes the context your agent reads. If stale code inside a build directory enters the result set, the agent treats it as current source. That is the part I would worry about first.
What to try once you know the binary will not run
In order:
- Confirm the exit code really is 126. Changing settings before you have pinned this down means backtracking later
- Check the mount options for both
/tmpand the cache directory. Since 1.1.13 the extraction happens on the cache side, so fixing/tmpalone changes nothing - When you file a security exception, mention that the extracted binary is verified with SHA-256. It gives whoever reviews the request something concrete to approve
- On machines that will never allow it, running on the fallback is a legitimate choice. From these measurements that is roughly 2 to 2.5x for a
grep-class path and 15 to 27x for a plain scan. The former is tolerable in a lot of situations - Revisit what your queries actually match. Query design moves results more than path speed does
On that last point, I have written separately about measuring the regex defaults /codesearch applies. The harder it is to fix your search path, the more it pays to get everything you need out of a single query — see Treating Code Search as a Contract: Measuring the /codesearch Regex Default if that is where you are.
If your machine also refuses to sign in, Running Antigravity CLI 1.1.13 on a Machine You Cannot Sign In From covers the other half of the constrained-environment story. On locked-down hardware these two tend to arrive together.
Start with the chmod -x reproduction on your own machine, just once. Once you know whether you get a 126, the next thing to touch picks itself.
Thank you for reading through the measurements with me.