Sample Header Ad - 728x90

When opening terminal applications, launchd is setting the initial $PATH inconsistently. Why?

3 votes
1 answer
239 views
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
	...
	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* 1. issued launchctl setenv PATH 2. issued either sudo launchctl config user or config system 3. modified any .plist file anywhere (or found any that set the PathEnvironmentVariable 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
        ...
	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](https://github.com/alacritty/alacritty/issues/8535) 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](https://apple.stackexchange.com/users/274760/gairfowl)’s suggestion (see comments) shows that same echo command outputting a completely different $PATH in both Alacritty and Terminal:
+/Users/Me/.zshenv:1>       # 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. --- ### Update 4 ### The developers working on Alacritty generously pointed me to [the section of their source code](https://github.com/chrisnc/alacritty/blob/6566dd3defa9f080dabb295740dc1dac06e3b8fb/alacritty_terminal/src/tty/unix.rs#L131-L150) responsible for launching new shells (lines 131 through 150). What it shows is, essentially (when zsh is the default shell),
/usr/bin/login -flp "$USER" /bin/zsh "-c exec -a -zsh /bin/zsh"
To paraphrase all the man pages, login -p will enter a $PATH into the new shell environment that it gets from pam(3) via environ(7) and execve(2). The man page for environ(7) explains that login(1), when -p is absent, sets the $PATH to:
/usr/bin:/bin
Which may explain what I’m seeing in Terminal.app (the source code for which I probably can’t peruse). But this doesn’t account for what echo $PATH is printing when in Alacritty, where login -p is being called:
/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin
Or what /usr/bin/env -i /bin/zsh -l -osourcetrace outputs in both Alacritty and Terminal:
/bin:/usr/bin:/usr/ucb:/usr/local/bin
At this point my only guess is that pam_launchd.so (listed in /etc/pam.d/login) is where the uncertain and inconsistent initial $PATH, as printed by echo $PATH when in Alacritty, is coming from. But I don't know. And I certainly don't know why. As for the odd (albeit consistent) /usr/bin/env -i /bin/zsh -l -osourcetrace output including /usr/ucb — a very old default leaking out of but ultimately overridden by Zsh? --- ### Point of clarification ### The example I’ve given of the path output by echo $PATH at the top of .zshenv when in Alacritty (thus acquired via login -p),
/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin
is an **example**. It’s **not** consistent. Sometimes it is that path, sometimes it is
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
and occassionally it is
/usr/bin:/bin:/usr/sbin:/sbin
What I would like to know is what it will be tomorrow, why, and whether I can lock it down without overriding it.
Asked by Absolute Input (39 rep)
Apr 6, 2025, 10:50 PM
Last activity: Apr 8, 2025, 10:10 AM