RCA-2026-04-03-001: Analysis

Root Cause

5 Whys Analysis

Why # Question and Answer

1

Why wasn’t bluetoothctl found?
Because: /usr/bin was not in the effective $PATH

2

Why wasn’t /usr/bin in $PATH?
Because: The PATH variable contained a literal string $PATH instead of the expanded value

3

Why was $PATH unexpanded?
Because: Somewhere in shell initialization (.zshrc, .zshenv, or .zprofile), a PATH assignment uses double-quoted or unquoted $PATH in a context where it wasn’t expanded β€” or a config file was written with single quotes around $PATH

4

Why does this only affect Claude Code?
Because: Claude Code spawns a non-interactive or semi-interactive subshell that may not source all profile files, or sources them in a different order than a login terminal

5

Why wasn’t this caught before?
Because: Most commands used in Claude Code sessions are invoked via dedicated tools (Read, Write, Grep, Glob) that don’t depend on $PATH β€” raw Bash tool use is less common

Root Cause Statement

The Claude Code Bash tool shell environment has a malformed $PATH where the system paths (/usr/bin, /usr/sbin, /bin, /sbin) are missing due to an unexpanded $PATH variable reference in shell initialization. This causes all standard system binaries to be unresolvable by name.

Contributing Factors

Factor Description Preventable?

Shell init order

Claude Code may not source .zshrc / .zshenv in the same order as an interactive terminal

Partially (investigate init files)

PATH construction

A line like export PATH="$HOME/.local/bin:$PATH" may be evaluated in a context where $PATH is empty or literal

Yes (use absolute paths in exports)

Rare Bash tool use

Most work uses dedicated tools; PATH issues go unnoticed

No (by design)

Time pressure

Incoming call compressed troubleshooting time

No

Shell Environment Deep Dive

How PATH Should Work

1. /etc/zsh/zshenv        ← System-wide, always sourced first
2. ~/.zshenv              ← User, always sourced
3. /etc/zsh/zprofile      ← System-wide, login shells only
4. ~/.zprofile            ← User, login shells only
5. /etc/zsh/zshrc         ← System-wide, interactive shells only
6. ~/.zshrc               ← User, interactive shells only

Standard /usr/bin comes from step 1 (/etc/zsh/zshenv) or is inherited from the parent process.

Debugging Commands

# Show raw PATH
echo $PATH

# Show PATH one-per-line (readable)
echo $PATH | tr ':' '\n'

# Check if /usr/bin is in PATH
echo $PATH | tr ':' '\n' | grep -x '/usr/bin'

# Find where PATH is set in zsh init files
grep -n 'PATH' ~/.zshenv ~/.zshrc ~/.zprofile 2>/dev/null

# Compare Claude Code shell vs terminal
# In terminal:
echo $PATH | tr ':' '\n' | wc -l

# Check what shell Claude Code uses
echo $SHELL
ps -p $$ -o comm=