I recently discovered that launchd
on macOS provides an initial, default $PATH
to new terminal windows that is not the default path set by macOS at log in, but instead is produced by launchd
according to (presumably) its own internal logic — without changes ever having been made via either launchctl
or any .plist
file that I could find.
This is the case whether using the Terminal.app
distributed by Apple, or a third-party terminal such as Alacritty.app
.
The default path on macOS at log in, as can be verified with sysctl -n user.cs_path
, is
/usr/bin:/bin:/usr/sbin:/sbin
But when I placed echo $PATH
on the first line of ~/.zshenv
, which is sourced before /etc/zprofile
and any other shell configuration file, I was seeing an entirely different $PATH
when opening a new window in Alacritty:
/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin
Which I verified to be coming from launchd
by searching the output of launchctl dumpstate
:
pid/72911 = {
type = pid
originator = /Applications/Alacritty.app
creator = alacritty[72911]
...
environment = {
PATH => /usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin
...
}
...
}
After restarting Alacritty, with only one window and one instance of the shell the same echo
command produced:
/usr/bin:/bin:/usr/sbin:/sbin
Which is what it should be. But then, opening the default Terminal.app
, I get
/usr/bin:/bin
Which is, again, not the OS default.
This doesn’t necessarily affect my ability to use the shell because I can set $PATH
in .zshenv
or .zprofile
to whatever I’d like, but it does require that I override the $PATH
that launchd
sets if I wish to control search order. In fact, what launchd
is doing makes controlling search order crucial because there is apparently no guarantee that the initial $PATH
handed to the shell will be consistent.
Does anybody know why launchd
is providing an inconsistent $PATH
? Is there any rhyme or reason to what it decides to include or not include in the $PATH
that it provides to every shell created with every new terminal window, and varying it by terminal application?
Again, I have never
- issued
launchctl setenv PATH <path>
- issued either
sudo launchctl config user <path>
orconfig system <path>
- modified any
.plist
file anywhere (or found any that set thePathEnvironmentVariable
key).
I am running macOS Sequoia 15.4 (24E248) on an M2 MacBook Pro, using the stock Zsh shell.
The closest answer I could find mentions that launchd
manipulates the default $PATH
, but the author doesn’t specify how or why.
Update 1
For the stock Terminal app, launchctl dumpstate
is showing
pid/45557 = {
type = pid
originator = /System/Applications/Utilities/Terminal.app
creator = Terminal[45557]
...
environment = {
PATH => /usr/bin:/bin:/usr/sbin:/sbin
...
}
...
}
Which is indeed the correct, default $PATH
. However, for the stock Terminal app, an echo $PATH
statement on the first line of .zshenv
continues to result in the truncated
/usr/bin:/bin
Update 2
The developers working on Alacritty have told me that they do not touch the $PATH
variable.
Update 3
With echo $PATH
as the first line in .zshenv
producing
/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin
in Alacritty, and
/usr/bin:/bin
in Terminal, subsequently executing
/usr/bin/env -i /bin/zsh -l -osourcetrace
per Gairfowl’s suggestion (see comments) shows that same echo
command outputting a completely different $PATH
in both Alacritty and Terminal:
+/Users/Me/.zshenv:1> <sourcetrace> # line 1
/bin:/usr/bin:/usr/ucb:/usr/local/bin # line 2 `echo $PATH`
There is no /usr/ucb
directory or file, hidden or not, that I could find. But given that a Google search for “ucb” tells me it is a reference to “University of California, Berkeley” (and that the output is from env -i zsh
), could it be Zsh itself is manipulating the initial $PATH
? And then either it or something else in macOS is inconsistently transforming /usr/ucb
into one of these not-configured paths before completing echo $PATH
on the first line of .zshenv
, and sometimes informing launchd
of the change (causing it to appear in the launchctl dumpstate
output)?
Note that those two lines from the env
output are printed before /etc/zprofile
is sourced, thus Apple’s path_helper
utility is not yet in play.
I should point out that in all my examples thus far the shell has been consistently in both login
and interactive
states, verified with setopt
.