-
-
Notifications
You must be signed in to change notification settings - Fork 11.1k
brew shellenv idempotency check skips fpath setup in child/nested zsh shells #21879
Description
brew doctor output
Your system is ready to brew.
Verification
- I ran
brew updatetwice and am still able to reproduce my issue. - My "
brew doctoroutput" above saysYour system is ready to brewor a definitely unrelatedTiermessage. - This issue's title and/or description do not reference a single formula e.g.
brew install wget. If they do, open an issue at https://github.com/Homebrew/homebrew-core/issues/new/choose instead.
brew config output
HOMEBREW_VERSION: 5.1.3
ORIGIN: https://github.com/Homebrew/brew
HEAD: 3a946229d190966cd725d5cc65e272d279a398ed
Last commit: 31 hours ago
Branch: stable
Core tap JSON: 01 Apr 04:58 UTC
Core cask tap JSON: 01 Apr 04:58 UTC
HOMEBREW_PREFIX: /opt/homebrew
HOMEBREW_CASK_OPTS: []
HOMEBREW_DISPLAY: /var/run/com.apple.launchd.nHdvo6frK8/org.xquartz:0
HOMEBREW_DOWNLOAD_CONCURRENCY: 36
HOMEBREW_FORBID_PACKAGES_FROM_PATHS: set
HOMEBREW_MAKE_JOBS: 18
Homebrew Ruby: 4.0.2 => /opt/homebrew/Library/Homebrew/vendor/portable-ruby/4.0.2_1/bin/ruby
CPU: 18-core 64-bit dunno
Clang: 21.0.0 build 2100
Git: 2.50.1 => /Applications/Xcode.app/Contents/Developer/usr/bin/git
Curl: 8.7.1 => /usr/bin/curl
macOS: 26.4-arm64
CLT: 26.3
Xcode: 26.4
Rosetta 2: false
What were you trying to do (and why)?
I have eval "$(brew shellenv zsh)" in both my ~/.zprofile (as recommended) and ~/.zshrc. I use Zellij (a terminal multiplexer) inside Ghostty. Zellij spawns non-login zsh shells for its panes, so only ~/.zshrc runs — not ~/.zprofile.
I expected eval "$(brew shellenv zsh)" in my .zshrc to set up zsh completions for brew-installed tools (bat, zellij, ripgrep, etc.) by adding /opt/homebrew/share/zsh/site-functions to fpath.
What happened (include all command output)?
brew shellenv zsh produces no output in Zellij panes (and in any nested/child zsh shell), because PATH already contains /opt/homebrew/bin — inherited from the parent shell's environment.
The early return in shellenv.sh skips all output, including the zsh-specific fpath[1,0]= line:
if [[ "${HOMEBREW_PATH%%:"${HOMEBREW_PREFIX}"/sbin*}" == "${HOMEBREW_PREFIX}/bin" ]]
then
return
fiThe problem is that PATH is an exported environment variable that child shells inherit, but fpath/FPATH is a zsh array that is not inherited. So the idempotency check (which only inspects PATH) incorrectly assumes fpath is also already configured.
This means zsh completions for all Homebrew-installed tools are broken in:
- Terminal multiplexer panes (Zellij, tmux)
- Nested zsh shells (
zshinsidezsh) - Any non-login child shell that inherits
PATHfrom a parent wherebrew shellenvalready ran
# In a nested zsh (or Zellij/tmux pane):
$ echo "$(brew shellenv zsh)" | wc -c
0
$ echo $fpath | tr ' ' '\n' | grep homebrew
# (no output)
# fpath is missing /opt/homebrew/share/zsh/site-functions
# so completions for brew-installed tools don't work
What did you expect to happen?
brew shellenv zsh should always emit the fpath[1,0]= line for zsh, even when the PATH idempotency check passes. The fpath setup is not redundant in child shells — unlike PATH, fpath is not inherited through the environment.
Ideally the early return should only skip the PATH/env-var exports that are actually inherited, and still emit the fpath line (and arguably re-check MANPATH/INFOPATH too, though those are less commonly affected).
Step-by-step reproduction instructions (by running brew commands)
# 1. Start a zsh login shell (simulates initial terminal launch)
zsh -l
# 2. Confirm brew shellenv works and fpath is set
eval "$(brew shellenv zsh)"
echo $fpath | tr ' ' '\n' | grep homebrew
# /opt/homebrew/share/zsh/site-functions ← present
# 3. Start a nested non-login zsh (simulates tmux/zellij pane)
zsh
# 4. PATH is inherited, but fpath is not
echo $PATH | tr ':' '\n' | grep homebrew
# /opt/homebrew/bin ← inherited
# /opt/homebrew/sbin ← inherited
echo $fpath | tr ' ' '\n' | grep homebrew
# (no output) ← NOT inherited
# 5. brew shellenv is a no-op because PATH check passes
eval "$(brew shellenv zsh)"
echo $fpath | tr ' ' '\n' | grep homebrew
# (still no output) ← bug: fpath was never set
# 6. Confirm brew shellenv produces zero output
echo "$(brew shellenv zsh)" | wc -c
# 0Workaround: Manually add to .zshrc:
fpath=($HOMEBREW_PREFIX/share/zsh/site-functions $fpath)Related: https://github.com/orgs/Homebrew/discussions/2547, #11930