[BUG] Bash tool: injected grep/find shell functions terminate the calling shell under bash < 4.0 (macOS system /bin/bash)

Open 💬 1 comment Opened May 27, 2026 by Wowfunhappy

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In native builds, the per-session shell snapshot injects wrapper functions that shadow grep and find (redirecting them to the embedded ugrep/bfs). Each function decides how to invoke the embedded tool with [[ $BASHPID != $$ ]]:

function find {
  ...
  if [[ -n $ZSH_VERSION ]]; then
    ARGV0=bfs "$_cc_bin" -S dfs -regextype findutils-default "$@"
  elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
    ARGV0=bfs "$_cc_bin" ... "$@"
  elif [[ $BASHPID != $$ ]]; then          # $BASHPID is empty on bash < 4.0
    exec -a bfs "$_cc_bin" ... "$@"          #   => always taken => replaces the shell
  else
    (exec -a bfs "$_cc_bin" ... "$@")        #   correct branch, never reached
  fi
}

$BASHPID was introduced in bash 4.0. Under bash 3.2 it expands to the empty string, so [[ "" != "<pid>" ]] is always true and the bare exec branch runs even in the main shell. exec replaces the calling shell, so every command sequenced after grep/find in the same shell (; …, && …, brace groups) is silently dropped. Both injected functions are affected.

This is not a fringe configuration. /bin/bash on macOS has been frozen at 3.2.57 since 2007 (Apple ships no GPLv3 software) and is the system bash on every current Mac. Any user whose $SHELL is /bin/bash runs the Bash tool under bash 3.2 — common on machines set up before the zsh default, in CI, or by preference.

What Should Happen?

Commands sequenced after grep/find should run normally; the wrapper must never replace the user's main shell.

1. Fix the guard by defaulting $BASHPID to $$, so it collapses to the safe subshell branch when $BASHPID is unavailable and preserves the bare-exec optimization on bash 4.0+:

elif [[ ${BASHPID:-$$} != $$ ]]; then

On bash < 4.0 the comparison is false and the always-correct ( exec … ) branch runs; on 4.0+ behavior is unchanged. Apply to every wrapper using this guard (grep, find, and any future ones).

2. Provide a supported opt-out for the shadowing. The Bash tool monkey-patches the user's shell with functions that shadow grep/find; this is invasive, depends on shell internals (this bug is one failure mode), and currently can't be turned off. The earlier builtin-ripgrep path honored USE_BUILTIN_RIPGREP=0, but a similar escape hatch was never added for the embedded bfs/ugrep functions. A documented env var (a USE_BUILTIN_RIPGREP-style flag, or CLAUDE_CODE_DISABLE_EMBEDDED_SEARCH=1) and/or settings key that suppresses these functions and falls back to system grep/find (and rg when present) would:

  • Make failures like this debuggable — isolating it would be a one-line experiment (disable shadowing → the silent truncation disappears → cause confirmed) and provide an immediate workaround while a fix ships. As things stand, this problem was EXTREMELY confusing to track down!
  • Respect users who prefer their own toolchain.
  • Bound the blast radius of any future shell-shadowing bug to a supported switch rather than a required release.

Error Messages/Logs

None — and that is central to the severity. The command succeeds (exit 0) and produces correct output; the shell is simply gone afterward, with no error, warning, or nonzero status. Observed vs. expected:

$ /bin/bash -c 'echo BEFORE; [[ $BASHPID != $$ ]] && exec true; echo AFTER'
BEFORE
# expected: "BEFORE" then "AFTER"; actual: "AFTER" missing — exec replaced the shell

Steps to Reproduce

  1. On macOS, ensure $SHELL=/bin/bash (system bash 3.2.57). Minimal check, guard in isolation:

``sh
/bin/bash -c 'echo BEFORE; [[ $BASHPID != $$ ]] && exec true; echo AFTER'
`
→ prints only
BEFORE`.

  1. In Claude Code (native build, ≥ ~2.1.117) with that shell, run any Bash tool call that sequences work after find/grep:

``sh
echo BEFORE; find . -maxdepth 1 >/dev/null; echo AFTER
`
→ prints only
BEFORE; AFTER (and anything after find`) silently does not run.

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.116

Claude Code Version

2.1.150 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

This report was drafted with Claude's help. This appears to be standard practice in this repository, but I wanted to call it out because I do not normally use LLMs in my written communication.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗