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`

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):

> 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/

> source /etc/zprofile
> echo $PATH

# output: /usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/

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`

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
    print no

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