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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions