Debugging launchd plist jobs

Jay Hennig bio photo Jay Hennig Comment

Launchd is basically OSX’s version of cron, and it’s taken me years to get a launchd job actually working (not of regular effort, of course, but still). My main issue was not really knowing how to debug my plist file. Instead, all I’d see were cryptic error messages like the following:

Jan 18 01:29:27 thisbemymachine com.apple.launchd.peruser.501[268] (com.medialog.app[86862]): Exited with code: 1

No longer! Here is my definitive solution to getting a plist file to run with launchd.

  1. First off, let’s create a dummy python file. Below, I assume this file is at /Users/mobeets/test.py. This can do something like write something to disk, or send an email–whatever you want. I also assume that this file might needs an environment variable set, like PYTHONPATH, which I’ve set below arbitrarily to /Users/mobeets/Box Sync/bin. (Note the absense of quotations in any of the <string> variables below, even when the string contains a space.)

  2. Now create the following dummy plist file (let’s call it filename.plist, where the job name is example) that runs on load.

<?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>Label</key>
        <string>example</string>
        <key>ProgramArguments</key>
        <array>
            <string>python</string>
            <string>/Users/mobeets/test.py</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>StandardInPath</key>
        <string>/tmp/test.stdin</string>
        <key>StandardOutPath</key>
        <string>/tmp/test.stdout</string>
        <key>StandardErrorPath</key>
        <string>/tmp/test.stderr</string>

        <key>EnvironmentVariables</key>
        <dict>
            <key>PYTHONPATH</key>
            <string>/Users/mobeets/Box Sync/bin</string>
        </dict>
    </dict>
</plist>
  1. Run plutil filename.plist to verify your plist file is valid.

  2. We want to be able to continuously monitor the outputs and errors of running our plist file. Open up a terminal and run tail -F /var/log/system.log /tmp/test.stdout /tmp/test.stderr | grep example.

  3. Now in another terminal we can load, unload, and start the job, all while monitoring the error logs to see if anything goes wrong. Load the plist file with launchctl filename.plist. Unload with launchctl filename.plist. Run with launchctl start example.

  4. If everything works as planned, you can customize your plist file to make it do what you actually want, iterating on the above steps until everything works. Launchd.info is a great resource for figuring out how to do various things.