1

I'm trying to configure launchd to trigger a python script/unix executable (i.e. python script with shebang line). When I load the .plist file (below), launchctl shows a status of 127 meaning, "The specified service did not ship with the operating system". However when I copy and paste the value I've entered for 'program' in the .plist file into the mac terminal it runs fine.

I've redirected stdout/stderr to the terminal (via the .plist) and it returns the message,

$ env: python3: No such file or directory

If I replace the value of Program in the plist to a simple 'hello world' esque batch script it works fine.

Why does the python program (urlwatch) run fine in terminal but return an error when called via launchd? How do I fix it?

Plist file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>local.careersWatch3</string>
        <key>Program</key>
        <string>/Users/justinmichael/Documents/urlwatch-master/urlwatch</string>
        <key>RunAtLoad</key>
        <true/>
        <key>StandardOutPath</key>
        <string>/dev/ttys000</string>
        <key>StandardErrorPath</key>
        <string>/dev/ttys000</string>
    </dict>
</plist>

I eventually want to run the script at set times of the day, but for now I'm using RunAtLoad = true for testing purposes until I can get it to work.

Loading into launchd and output:

$ launchctl load  ~/Library/LaunchAgents/local.careerswatch3.plist
$ env: python3: No such file or directory

Call to check status of agent and output:

$ launchctl list | grep local.careersWatch3
-   127 local.careersWatch3

Looking up the meaning of code '127' in terminal:

$ launchctl error 127
127: The specified service did not ship with the operating system
  • Any reason why you aren't using cron? – MMB Feb 12 '18 at 7:38
  • As I understand it, on Macs, Cron has been deprecated and replaced with Launchd. apple.stackexchange.com/questions/12819/… – JMikes Feb 12 '18 at 15:26
  • I've tried running the same program using cron and nothing happens. In the system log I get: "cron[7747]: no path for address 0x109aa6000". I suspect this indicates the same problem of being unable to find a related file. My best guess (more generally) is that there's a problem with environment variables (e.g. the path) not being the same between running in terminal and calling via launchd/cron but I'm not sure how to fix it. – JMikes Feb 12 '18 at 16:25
  • macOS 10.13 ships with python 2.7.10, so there is no 'python3' executable, by default. Did you add it via ports or homebrew? If so, it's likely in /usr/local/bin. I'd work on getting the script to work in the terminal as the user you plan to run the launchd script as. It will be much easier to debug. – MMB Feb 14 '18 at 7:51
  • Yes, python3 has been installed and there is an executable by that name in /usr/local/bin. The python script that I'm running (in the form of a unix executable) works fine in the terminal which is part of my confusion. – JMikes Feb 17 '18 at 2:30
1

The problem was with the environment variables, specifically that the $PATH is different for jobs run by cron vs programs called by me in terminal as a logged in user. Calling echo $PATH in a cron job and checking if it includes the directory of the python interpreter can help to confirm this is the problem.

Two solutions:

1) Quick and Dirty

Find where the Python interpreter is installed and change the shebang line at the top of the unix executable/python script to call it directly. i.e

#!/usr/bin/env python3

becomes

#!/usr/local/bin/python3

Here, it doesn't matter if the python interpreter is on the path or not because it's location is given explicitly. The downside is that its location is now hard-coded and if you move the script to a different computer the script may not work, neither in cron nor when run in the terminal, if python has been installed in a different location.

2) Less Quick, Less Dirty

Write a shell script that adds the location of the python interpreter to the path if it isn't already there (as per this SuperUser question) and then calls the python script. This way the script hasn't been changed and won't be accidentally broken by moving it to a computer where python is installed in a different directory.

#!/bin/bash
# directory python is found in
dir="usr/local/bin"
#add to path if not there
if [ -d "$dir" ] && [[ ":$PATH:" != *":$dir:"* ]]; then
        PATH="${PATH:+"$PATH:"}$dir"
fi
#Run program
/path/to/program/program_name

Be sure to make the script executable via chmod +x /path/to/script/script.sh

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.