[BUG] Bundled grep/find/rg shims fail with `-G: error while loading shared libraries` when CLAUDE_CODE_EXECPATH is the dynamic loader (native binary launched via ld.so; NixOS)

Open 💬 0 comments Opened Jul 4, 2026 by Reclyptor

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

Claude Code injects grep/find/rg shell functions (the bundled-search shims) into the Bash-tool shell. Each runs the binary at $CLAUDE_CODE_EXECPATH with a spoofed argv[0], e.g. the grep shim:

( exec -a ugrep "$_cc_bin" -G --ignore-files --hidden -I --exclude-dir=.git … "$@" )

_cc_bin is $CLAUDE_CODE_EXECPATH, which is derived from the process's own exec path. When the native binary is launched through the dynamic loader — the normal way to run a foreign dynamically-linked binary on NixOS, and common with other FHS/wrapper launchers — execPath is the loader itself, so CLAUDE_CODE_EXECPATH becomes e.g. …/ld-linux-x86-64.so.2.

The shim then effectively runs ld-linux-x86-64.so.2 -G …. ld.so treats -G as a shared object to load, fails, and prints the error below using that same token as the program name. Every bare grep/find/rg in a Bash-tool command then fails with exit 127.

The existing guard [[ -x $_cc_bin ]] does not catch this, because the loader is executable.

What Should Happen?

grep/find/rg should keep working — fall back to the real system tools (command grep, etc.) when $CLAUDE_CODE_EXECPATH does not point at an invocable bundled-search binary.

Error Messages/Logs

$ grep -n foo somefile
-G: error while loading shared libraries: -G: cannot open shared object file: No such file or directory
# exit 127

$ echo "$CLAUDE_CODE_EXECPATH"
/nix/store/…-glibc-2.42-61/lib/ld-linux-x86-64.so.2      # the loader, not the claude binary

Steps to Reproduce

  1. Run the native claude binary in any way that goes through the dynamic loader (on NixOS this is the norm — e.g. the launcher does exec ld-linux-x86-64.so.2 --library-path … <binary> "$@").
  2. In a Bash tool call, run any bare grep, find, or rg (e.g. grep -n foo file).
  3. Observe: -G: error while loading shared libraries: -G: …, exit 127.

Minimal demonstration of the underlying mechanism on any glibc Linux (simulating CLAUDE_CODE_EXECPATH pointing at the loader, as it does in these setups):

loader=/lib64/ld-linux-x86-64.so.2
exec -a ugrep "$loader" -G --ignore-files --hidden -I foo /dev/null
# → -G: error while loading shared libraries: -G: cannot open shared object file …

Claude Model

Opus (the bug is model-independent — it lives in the Bash tool's shell shim)

Is this a regression?

Yes, this worked in a previous version

Last Working Version

Before the bundled grep/find/rg shims were introduced (~v2.1.117 per the changelog); prior versions had no shims, so the commands used the real binaries.

Claude Code Version

2.1.201 (Claude Code)

Platform

Anthropic API

Operating System

Other Linux (NixOS, x86_64, nix-ld enabled)

Terminal/Shell

Other (kitty; the failure is in the Bash tool's spawned non-interactive shell, independent of the outer terminal)

Additional Information

Root cause. The shim assumes $CLAUDE_CODE_EXECPATH is a multicall-capable claude binary, but derives it from process.execPath. Whenever the binary is launched via ld.so, execPath//proc/self/exe is the loader, so the shim execs the loader with search flags. Verified on 2.1.201: CLAUDE_CODE_EXECPATH = …/ld-linux-x86-64.so.2, and the CLAUDE_CODE_EXECPATH shim code is present in the 2.1.201 binary.

Suggested fixes (any one resolves it):

  1. Harden the fallback — [[ -x $_cc_bin ]] is insufficient; also skip the shim when $_cc_bin's basename matches a loader (ld-*.so* / ld-linux*), or verify it's actually the claude/multicall binary, and fall back to command grep/command find/command rg otherwise.
  2. Don't derive CLAUDE_CODE_EXECPATH from a loader — if process.execPath resolves to a dynamic loader, resolve the real program path (or leave the shims disabled).
  3. A documented opt-out env var (e.g. CLAUDE_CODE_DISABLE_SEARCH_SHIMS=1; see the previously-proposed CLAUDE_CODE_DISABLE_FIND_SHIM in #53961).

Related: #57362 and #62642 (same shims, other failure modes), #53961 (proposed opt-out), #20012 and #62952 (NixOS / non-FHS).

Workaround for other NixOS users: launch the native binary directly (its interpreter resolves via nix-ld) instead of via ld-linux … <binary>; that keeps execPath/argv[0] on the real binary, so CLAUDE_CODE_EXECPATH is correct and the ugrep/bfs/rg multicall works. Per-command, command grep / command find / command rg bypass the shim.

View original on GitHub ↗