Skip to content

brew shellenv idempotency check skips fpath setup in child/nested zsh shells #21879

@sumanthratna

Description

@sumanthratna

brew doctor output

Your system is ready to brew.

Verification

  • I ran brew update twice and am still able to reproduce my issue.
  • My "brew doctor output" above says Your system is ready to brew or a definitely unrelated Tier message.
  • 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
fi

The 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 (zsh inside zsh)
  • Any non-login child shell that inherits PATH from a parent where brew shellenv already 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
# 0

Workaround: Manually add to .zshrc:

fpath=($HOMEBREW_PREFIX/share/zsh/site-functions $fpath)

Related: https://github.com/orgs/Homebrew/discussions/2547, #11930

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedWe want help addressing this

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions