Skip to main content
  1. Posts/

PATH Variable Changed inside Tmux on macOS?

·424 words·2 mins·
Table of Contents

Recently, I met a strange issue related to Tmux. After open a tmux session, the PATH variable is changed and is different from outside tmux.

This strange behavior breaks my Python:

  • outside tmux: python3 points to Python installed by Miniconda
  • inside tmux: python3 points to Python3 installed by Apple

The cause
#

The reason is that when you start a tmux session, it will start a shell (in the latest macOS, it is zsh by default) in login mode. So certain zsh config files are sourced again (the culprit is /etc/zprofile), which leads to messed up PATH variable. This is also caused by macOS’s special way to construct the PATH variable.

The content of /etc/zprofile is:

if [ -x /usr/libexec/path_helper ]; then
	eval `/usr/libexec/path_helper -s`
fi

When zsh is used as a login shell, the file /etc/zprofile will be sourced. So when we run tmux command and it initialize a zsh shell, this file is source again, which messes up our PATH variable due to the behavior of path_helper.

In order to confirm this, we can do an experiment (credit here):

> PATH=BEG:$PATH:END
> echo $PATH

# output: BEG:/opt/homebrew/Caskroom/miniconda/base/bin:/opt/homebrew/Caskroom/miniconda/base/condabin:/Users/hao/.local/share/zinit/polaris/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:END

> source /etc/zprofile
> echo $PATH

# output: /usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:BEG:/opt/homebrew/Caskroom/miniconda/base/bin:/opt/homebrew/Caskroom/miniconda/base/condabin:/Users/hao/.local/share/zinit/polaris/bin:/opt/homebrew/bin:/opt/homebrew/sbin:END

What path_helper does is to construct PATH from /etc/paths and /etc/path.d. Append the current PATH variable to the constructed PATH and remove the duplicated items: I think it is just looping through the item in the PATH variable and remove items that have appeared before.

This explains why unique item inside BEG:END structure is kept and duplicate items are removed.

How to prevent
#

One thing I see people do is to change /etc/zprofile, adding one line to empty PATH:

if [ -x /usr/libexec/path_helper ]; then
    PATH=""  #empty the PATH
    eval `/usr/libexec/path_helper -s`
fi

For me, it is hacky. I do not want to change the system default settings.

Another method is to tell tmux to start a non-login shell instead via default-command option:

# default command should be path to zsh executable on your system
set -g default-command /bin/zsh

If zsh is started as a non-login shell, /etc/zprofile won’t be sourced, so PATH is not changed. To check if we are running a login shell or not (zsh-only), run the following (source here):

if [[ -o login ]]; then
    print yes
else
    print no
fi

After this setup, to make sure it work, run killall tmux and exit your terminal and restart it again.

References
#

Related

Tmux: Open Terminal Failed in Kitty Terminal
··258 words·2 mins
使用代理加速 Mac 终端下载速度
··87 words·1 min
Boosting Your Productivity on Terminal with Zsh and Plugins
··716 words·4 mins