[Bug] Bash tool: zsh command resolution fails after compound pipelines with while-read and &&-chains
Bug Description
Title: Bash tool: zsh "command not found" for external commands after compound pipelines, even with
full PATH
Summary
After certain compound Bash commands (multi-stage pipe ending in while read,
followed by &&-chained external commands), the post-&& commands fail with
(eval):1: command not found: <cmd> even though $PATH is fully populated and
the commands resolve fine in direct probes. Substituting absolute paths
(/bin/ls vs ls) makes the same command work — so PATH-the-variable is
correct, but the shell's command-lookup machinery is broken in the post-pipeline
eval context.
Environment
- macOS, zsh 5.9
- Claude Code CLI (current build)
- Shell: /bin/zsh (interactive zsh; verified $SHELL, $0, $ZSH_VERSION)
Minimal repro (fails)
cd ~/.claude && true ; printf 'X~/.claude/doctrine/a.mdX\n' | sort -u | \
sed 's/X//g; s|~/|/Users/joewolf/|' | \
while read path; do
if [ -e "$path" ]; then echo "OK"; else echo "MISSING"; fi
done && ls doctrine/ | wc -l
Output:
MISSING
(eval):1: command not found: ls
(eval):1: command not found: wc
Same command with /bin/ls and /usr/bin/wc substituted — WORKS:
... done && /bin/ls doctrine/ | /usr/bin/wc -l
Output:
MISSING
Diagnostic findings
$PATHis fully populated in every direct probe (top-level, inside
while-read body, after &&-chain, inside command substitution). Full PATH
includes /usr/bin and /bin where ls/wc/basename live.
which ls,which basename,which wcall resolve to /bin/ls,
/usr/bin/basename, /usr/bin/wc in direct probes.
- The error prefix
(eval):1:is zsh's specific format for commands run
through eval — the post-pipeline &&-chained commands ARE being eval'd
somewhere in the tool chain, not run directly.
- Absolute paths bypass the broken lookup and work normally → confirms
PATH-the-variable is fine, but PATH-based command resolution is broken in
that eval context.
- Likely cause: zsh command hash-table is in a degraded/cleared state in the
re-eval context, but $PATH itself is preserved. zsh normally hashes
PATH-directory contents on first lookup; if hash is invalidated without
rehash, subsequent PATH-relative commands fail until rehash.
Why non-deterministic-looking on first bisection
Structural triggers don't bisect cleanly — minor differences in command
structure (one sed substitution vs two, presence of ~/ in input, etc.)
flip between working and failing in confusing ways. This suggests the
trigger isn't purely structural in zsh — it's likely in how Claude Code's
Bash tool processes the command string before passing it to zsh (token
length threshold, sandbox heuristic, hash-eviction timing, or hook
intercepting+re-eval'ing parts of compound commands).
Smoking-gun probe pair (deterministic)
Same command, two outcomes — only difference is bare command names vs absolute paths:
# FAILS:
... | while read path; do ...; done && ls doctrine/ | wc -l
# WORKS:
... | while read path; do ...; done && /bin/ls doctrine/ | /usr/bin/wc -l
Workarounds (in order of preference)
- Absolute paths for external commands after long pipelines
- Split compound commands into separate Bash tool calls
- Avoid
while readfollowed by&&-chains with external commands in
a single Bash invocation
Severity
Low (workaround is trivial), but high "wasted-debugging-time" potential —
the failure mode looks like a PATH bug but isn't, and the non-deterministic-
looking bisection chews through context budget before the smoking-gun absolute-
path test reveals the real cause.
Repro file count: tested against 14 probes; both reproducible failures and
working variants captured. Happy to share the full probe transcript if useful.
Environment Info
- Platform: darwin
- Terminal: Apple_Terminal
- Version: 2.1.143
- Feedback ID: 521ccab5-059c-4f7f-b447-b58d2c111a34
Errors
[{"error":"Error: Stream idle timeout - partial response received\n at DSK (/$bunfs/root/src/entrypoints/cli.js:9089:15603)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-05-16T14:32:45.578Z"},{"error":"Error: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()\n at from (/$bunfs/root/src/entrypoints/cli.js:118:7862)\n at <anonymous> (/$bunfs/root/src/entrypoints/cli.js:126:12898)\n a…
Note: Content was truncated.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗