Launchd ran job quits early

Hey,

I have a user script that I have set up to run daily with launchd, the launchd logs indicate that the script is started but it's almost immediately exited with status 0. The odd thing is that it doesn't say that the script was killed or stopped in anyway, even though that is what seems to happen.

2024-12-04 14:15:04.970214 (gui/501/com.me.test) <Notice>: internal event: WILL_SPAWN, code = 0 2024-12-04 14:15:04.970320 (gui/501/com.me.test) <Notice>: service state: spawn scheduled 2024-12-04 14:15:04.970325 (gui/501/com.me.test) <Notice>: service state: spawning 2024-12-04 14:15:04.970431 (gui/501/com.me.test) <Notice>: launching: xpc event 2024-12-04 14:15:04.972067 (gui/501/com.me.test [26446]) <Notice>: xpcproxy spawned with pid 26446 2024-12-04 14:15:04.972096 (gui/501/com.me.test [26446]) <Notice>: internal event: SPAWNED, code = 0 2024-12-04 14:15:04.972107 (gui/501/com.me.test [26446]) <Notice>: service state: xpcproxy 2024-12-04 14:15:04.972160 (gui/501/com.me.test [26446]) <Notice>: internal event: SOURCE_ATTACH, code = 0 2024-12-04 14:15:04.998157 (gui/501/com.me.test [26446]) <Notice>: service state: running 2024-12-04 14:15:04.998180 (gui/501/com.me.test [26446]) <Notice>: internal event: INIT, code = 0 2024-12-04 14:15:04.998199 (gui/501/com.me.test [26446]) <Notice>: Successfully spawned shell_wrapper.sh[26446] because xpc event 2024-12-04 14:15:05.217482 (gui/501/com.me.test [26446]) <Notice>: exited due to exit(0), ran for 246ms 2024-12-04 14:15:05.217494 (gui/501/com.me.test [26446]) <Notice>: service state: exited 2024-12-04 14:15:05.217502 (gui/501/com.me.test [26446]) <Notice>: internal event: EXITED, code = 0 2024-12-04 14:15:05.217506 (gui/501 [100003]) <Notice>: service inactive: com.me.test 2024-12-04 14:15:05.217523 (gui/501/com.me.test [26446]) <Notice>: service state: not running

In the script itself I log at the end to indicate that it finished successfully, when ran with launchd I never reach this point however. The script also do two network requests and make a pause for between 5-10 seconds with time.sleep(paus) (python script). However the launchd log indicates that the script finished in 246ms.

Not sure what is going on, the script itself does not require any privileges above a normal user. I have tried running the script directly, and it works as expected. I have also tried evoking the script from launchd explicitly with: launchctl kickstart which starts the job but gives the same result as the scheduled job, except the log now says: `launching: non-ipc demand' instead.

I have also tried to remove the networking requests and replace them by reading data from a file without any sleep, no difference in outcome though.

I have generated the launchd plist file using Lingon.app and it's placed in ~/Library/LaunchAgents

Not sure what more information I can provide, but need some suggestion on how I can proceed help solving this.

Answered by DTS Engineer in 816965022

It’s likely that whatever’s causing your Python code to fail is also emitting something useful to stdout or stderr, and you can capture that to a file via the StandardOutPath and StandardErrorPath properties in your launchd property list.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I'm going to update the post with some additional information, not sure if I can actually edit the post.

The script itself is written in python and this script is launched by a shell_wrapper.sh which does exit with status 0. Turns out that if I instead launch the python script directly, it exits with status 127. So using the wrapper was just hiding the exit code.

I have the Xcode developer tools installed and the supplied system version of Python which I believe is 3.9. In addition to this I have also installed, Python version 3.10 with the pkgsrc package manager, to resolve which Python version to use, I do #!/usr/bin/env python, now this seems to have confused launchd, so I hardcoded the path to my version of Python like this: #!/opt/pkg/bin/python and now I get an exit code 2, when the script is launched by launchctl kickstart. I also have some python modules that I have installed like selectolax, maybe those can't be found by launchd, which may have different env variables set than my user environment.

One more update. I have made a minimal demo which shows the issue.

#!/usr/bin/env python3

# trying: python --version
# in my shell gives this: Python 3.10.15

# /opt/pkg/bin/python3   fails exit(2)
# /opt/pkg/bin/python    fails exit(2)
# /usr/bin/env python3   works exit(0), resolves to system supplied version! (3.6.9)
# /usr/bin/env python    fails exit(127)
# /usr/bin/python3       works exit(0), system supplied version!

import sys
import os

log_path = "/tmp/test_log.txt"  # Simple location

try:
    with open(log_path, "a") as f:
        f.write(str(sys.version))
        f.flush()
    print("Write successful")
except Exception as e:
    print(f"Error writing to file: {e}")

Accepted Answer

It’s likely that whatever’s causing your Python code to fail is also emitting something useful to stdout or stderr, and you can capture that to a file via the StandardOutPath and StandardErrorPath properties in your launchd property list.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for looking at this. Turns out that launchd couldn't find my user installed version of Python. I added the EnvironmentVariables key to the launchd .plist file and provided a $PATH variable. Doing this, the example code now logs my Python version and moving on to the actual script, then also worked. :)

Launchd ran job quits early
 
 
Q