MacOS – Reproducing execution launchd execution environment

command linelaunchdmacos

I have a script which runs flawlessly under a normal interactive shell, but which frequently has errors when run as a user LaunchAgent.

Is there a way to manually, and ideally interactively, run a command exactly as it is run by launchd, to make it easier to debug?


To be clear, the script in question does work (intermittently) under launchd. It is not as simple as missing paths. I know that launchd uses a more spartan environment, but the situation is now:

  • There are frequent intermittent failures under launchd
  • I have never reproduced these failures in my user shell

As is common, there are numerous differences in environment between my fully configured user shell and the minimal launchd context, so it is very difficult to guess what difference might be at fault (or whether it even is an environment variable that causes the difference), which is why I was hoping to find a way simply to run commands manually and interactively, exactly as they would run under launchd. I am surprised that there is not a launchd run subcommand or similar.

Best Answer

Launchd jobs run with a default shell that does not import any of the typical environment variables you'll see in an interactive shell; in particular the PATH variable lacks any directories that your interactive shell might have added, which can cause headaches. You can work around that two ways:

  • By making your script as thoroughly generic as possible: using full path specifications to every command and utility, and explicitly setting variables as you need them rather than relying on the environment.
  • By adding an EnvironmentVariables dictionary to your launchd plist file and specifying the environment there.

The second looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    < ... >
    <key>EnvironmentVariables</key>
    <dict>
           <key>VARIABLE_NAME_1</key>
           <string>variable_contents</string>
           <key>VARIABLE_NAME_2</key>
           <string>variable_contents</string>
    </dict>
    < ... >
</dict>
</plist>