3

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

  1. issued launchctl setenv PATH <path>
  2. issued either sudo launchctl config user <path> or config system <path>
  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[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.


Update 4

The developers working on Alacritty generously pointed me to the section of their source code 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.

10
  • 4
    launchd isn't setting PATH; that's not what your output shows. "Alacritty" is setting it for itself, likely because that's what is in its settings or its hard-coded defaults. Other ways for PATH to be set are in the shell initialization files, in /etc/paths, and in /etc/paths.d. If Terminal is giving you a different PATH for the same shell, the difference is to be found either in its settings or in those of Alacritty Commented Apr 6 at 23:38
  • 1
    You can see the top-level calls in the startup file sequence with /usr/bin/env -i /bin/zsh -l -osourcetrace; the call to /usr/libexec/path_helper in /etc/zprofile may be significant. I think Terminal launches each new tab as a 'login' shell (the -l); Alacritty might not be doing that. Commented Apr 6 at 23:38
  • @LincDavis I’ve updated the question with what launchctl dumpstate reports for the stock Terminal app. With respect to Alacritty, I asked its developers and was told that they “do not touch” the variable. I certainly couldn’t find any setting in the Alacritty configuration that might modify the initial $PATH. Commented Apr 7 at 18:12
  • 1
    Something odd: the functions in /etc/zshrc_Apple_Terminal are part of a zsh session started from Terminal, but that file does not appear in the output from the env command with -osourcetrace. That suggests that Terminal is launching zsh with some additional parameters or commands. Commented Apr 7 at 19:53
  • 1
    What is the practical problem we're trying to solve here? You seem to be asking a hypothetical question about why something happens. What do you want to happen, and how is it different from what is happening? Commented Apr 8 at 1:31

1 Answer 1

2

What I would like to know is what it will be tomorrow, why, and whether I can lock it down without overriding it.

The PATH environment variable is subject to change between releases of macOS. For Power Manager, we recently had to actively set more environment variables after macOS changed its behaviour.

If you require a predictable PATH environment variable, proactively set it based on your needs.

darwin/sysroot/include/paths.h

Within the underlying darwin source code are the following constants:

#define _PATH_DEFPATH "/usr/local/bin:/bin:/usr/bin" #define _PATH_STDPATH "/bin:/usr/bin:/sbin:/usr/sbin" 

It is likely everything appended to these values is application specific; be that by the developers' choice or through user configuration files.

2
  • 1
    Good find. But isn't the question about why starting a terminal emulator sometimes leads to one of them, and somtimes to the other? Commented Apr 8 at 6:54
  • 1
    @nohillside You are right about the question. My intention with this answer to provide the base values provided in darwin's source code. The lengthy comments seemed an inappropriate place to note this. Commented Apr 8 at 10:06

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.