Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

Why doesn't my launchd plist work as expected? How to troubleshoot?

I have the following plist file as ~/Library/LaunchAgents/wealthychef.obsidian-daily.plist. I'm wondering why it does not execute when loaded. I feel like it should run every minute from 18:00 to 18:59 as written, and if the computer is asleep at those times, then it should run when the computer wakes up... yet it does not. It does not run at all, even though I am always logged in on the machine. Can someone help me understand what I'm doing wrong? Thank you.


<?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>wealthychef.obsidian-daily</string>

    <key>Program</key>
    <string>/Users/rcook/bin/obsidian-daily.sh</string>

    <key>StandardErrorPath</key>
    <string>/Users/rcook/bin/log/obsidian-daily.err</string>

    <key>StandardOutPath</key>
    <string>/Users/rcook/bin/log/obsidian-daily.out</string>
    <key>StartCalendarInterval</key>
    <dict>
        <!--Missing arguments are considered wildcards-->
        <key>Hour</key>
        <integer>18</integer>
    </dict>
</dict>
</plist>



I have validated it and loaded it:


rcook: launchctl unload ~/Library/LaunchAgents/wealthychef.obsidian-daily.plist
rcook: launchctl load ~/Library/LaunchAgents/wealthychef.obsidian-daily.plist
rcook: plutil ~/Library/LaunchAgents/wealthychef.obsidian-daily.plist
/Users/rcook/Library/LaunchAgents/wealthychef.obsidian-daily.plist: OK


The script /Users/rcook/bin/obsidian-daily.sh exists and is executable and works as expected when launched from the Terminal. Here are its contents. The command in the script worked fine when executed from a cron job. Cron won't run if the Mac is asleep, so I want to use launchd, which I am told should run when the Mac wakes up. In fact, this plist doesn't run at all, awake or asleep. The funky URL I'm opening is to be launched by Obsidian.md app.


#!//usr/bin/env bashecho "It is running on $(date)" > ${HOME}/script-turd.txt
open 'obsidian://advanced-uri?vault=wealthyvault&daily=true&mode=prepend' 


I don't know how to troubleshoot any further. Thanks for any help.

MacBook Pro 16″

Posted on Dec 7, 2023 7:55 PM

Reply
Question marked as Top-ranking reply

Posted on Dec 8, 2023 7:42 PM

wealthychef wrote:

I don't know what you mean by implying I'm not applying logic to this. Presumably you mean "common sense." The logic I'm familiar with is cron. The man page for launchd.plist does not say anything about "higher level arguments" anywhere. Where did you get that information? What logic should I apply?

I was just referring to your original post. Now that I read it again, it looks like you are saying that it should run every minute, not that you want it to run every minute.

I would love for the script to run at 18:00, every day, as written here. However, it does not. It has never launched once since loading. The echo command is there to make doubly sure of that by leaving evidence in my home directory. That file does not exist. Thus I can conclude that it never runs. Not at 18:00 or at any other time. If I am specific about the minute, that does not work either:

Launchd tasks are tricky for several reasons. If your script isn’t running at all, then you don’t know where it is failing. You’ll need to isolate those areas one at a time to find the cause.


First, remove the calendar time entirely. Next, instead of running your script, just echo date to a file. When that works, then you can try to specify the time. Once you get that working, comment it out and try to get your script working.


I agree that the problem is likely with your script. Launchd runs scripts in a different context than in a Terminal. What works from an interactive Terminal might not work in launchd. So fix the double slash. And double check your executable permissions. And double check those commands. I’m not familiar with bashecho. If that is 3rd party software, then the environment change could explain the problem. Take it one step at a time.

Similar questions

16 replies
Sort By: 
Question marked as Top-ranking reply

Dec 8, 2023 7:42 PM in response to wealthychef

wealthychef wrote:

I don't know what you mean by implying I'm not applying logic to this. Presumably you mean "common sense." The logic I'm familiar with is cron. The man page for launchd.plist does not say anything about "higher level arguments" anywhere. Where did you get that information? What logic should I apply?

I was just referring to your original post. Now that I read it again, it looks like you are saying that it should run every minute, not that you want it to run every minute.

I would love for the script to run at 18:00, every day, as written here. However, it does not. It has never launched once since loading. The echo command is there to make doubly sure of that by leaving evidence in my home directory. That file does not exist. Thus I can conclude that it never runs. Not at 18:00 or at any other time. If I am specific about the minute, that does not work either:

Launchd tasks are tricky for several reasons. If your script isn’t running at all, then you don’t know where it is failing. You’ll need to isolate those areas one at a time to find the cause.


First, remove the calendar time entirely. Next, instead of running your script, just echo date to a file. When that works, then you can try to specify the time. Once you get that working, comment it out and try to get your script working.


I agree that the problem is likely with your script. Launchd runs scripts in a different context than in a Terminal. What works from an interactive Terminal might not work in launchd. So fix the double slash. And double check your executable permissions. And double check those commands. I’m not familiar with bashecho. If that is 3rd party software, then the environment change could explain the problem. Take it one step at a time.

Reply

Dec 10, 2023 8:22 AM in response to wealthychef

wealthychef wrote:

It means that launchd does not read the shebang, nor the script itself; it is just running executables with arguments, "the way execv might do it." Even though my shell script is executable, etresoft seems to be saying that launchd is not running a shell, so I cannot depend on things like path expansion and redirection.

As I said, it's complicated. Your double-slash shebang is wrong, most definitely. You can run a shell script using just the path to the script itself as long as your shebang is correct and the permissions on the script are correct. That can be something you attempt later, once you get the launchd plist working.


This is one of the most complicated and difficult parts of an already complicated and difficult to understand operating system. Even most professional IT admins and software developers don't understand this topic.


I cannot fully express just how deeply complicated this topic is. Pretty much every statement I've told you can be considered wrong in some context. I'm just trying to simplify it enough so that you can get your script to run at 6pm.


In many cases, you can do things slightly differently and it will still work. But that does not mean it is correct. It may mean that you have inadvertently stumbled upon some obscure, little-documented fallback behaviour. Then, you change something else, that shouldn't matter, and it stops working. Why?


When writing scripts, never, ever assume anything about existing paths. Always specify the full path to any command you run. Never, ever assume anything about environment variables. If you need one, you specify it, then and there. This applies to any kind of shell scripting. When you are writing shell scripts to run in a launchd context, it just gets lots more difficult because now you have to deal with the launchd context, which isn't like your own shell environment, and has its own set of search paths, completely separate from anything in the command line context.

Reply

Dec 7, 2023 8:19 PM in response to wealthychef

Try:

#!/usr/bin/env bash
# echo "It is running on $(date)" > ${HOME}/script-turd.txt
open 'obsidian://advanced-uri?vault=wealthyvault&daily=true&mode=prepend' 


Or:

#!/usr/bin/bash
# echo "It is running on $(date)" > ${HOME}/script-turd.txt
open 'obsidian://advanced-uri?vault=wealthyvault&daily=true&mode=prepend' 

Reply

Dec 8, 2023 8:38 AM in response to wealthychef

Sometimes comments like that aren't meant to be taken literally. You have to apply logic to them as well.


So when it says "Missing arguments are considered wildcards" that only means higher-order arguments. In other words, you are asking for the script to be run at 18:00, every day, every month. Otherwise, every daily script would run constantly.


What you'll need to do is change your script so that, when it gets started at 18:00, it runs for exactly one hour, pausing one minute between runs. You may need extra logic if you want precise, per minute timing.

Reply

Dec 8, 2023 7:14 PM in response to etresoft

Thank you for your reply.

I don't know what you mean by implying I'm not applying logic to this. Presumably you mean "common sense." The logic I'm familiar with is cron. The man page for launchd.plist does not say anything about "higher level arguments" anywhere. Where did you get that information? What logic should I apply?


By the way, I got my code from this page: (moderator bot won't let me put in links???)


I would love for the script to run at 18:00, every day, as written here. However, it does not. It has never launched once since loading. The echo command is there to make doubly sure of that by leaving evidence in my home directory. That file does not exist. Thus I can conclude that it never runs. Not at 18:00 or at any other time. If I am specific about the minute, that does not work either:


<key>StartCalendarInterval</key>
    <dict>
        <!--Missing arguments are considered wildcards-->
        <key>Hour</key>
        <integer>09</integer>
        <key>Minute</key>
        <integer>56</integer>
    </dict>


Thanks


[Edited by Moderator]

Reply

Dec 9, 2023 12:43 AM in response to etresoft

OK, I have pared the plist to 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>Label</key>
    <string>wealthychef.obsidian-daily</string>

    <key>Program</key>
    <string>date > /Users/rcook/test.txt</string>

    <key>StandardErrorPath</key>
    <string>/Users/rcook/bin/log/obsidian-daily.err</string>

    <key>StandardOutPath</key>
    <string>/Users/rcook/bin/log/obsidian-daily.out</string>

</dict>
</plist>


When run, I get this:


rcook@MacBook-Pro-2021 (wealthyvault (master)): launchctl load ~/Library/LaunchAgents/wealthychef.obsidian-daily.plist
Load failed: 5: Input/output error
Try running `launchctl bootstrap` as root for richer errors.
rcook@MacBook-Pro-2021 (wealthyvault (master)): launchctl bootstrap
Usage: launchctl bootstrap <domain-target> [service-path, service-path2, ...]


To add to my confusion:


plutil ~/Library/LaunchAgents/wealthychef.obsidian-daily.plist
/Users/rcook/Library/LaunchAgents/wealthychef.obsidian-daily.plist: OK



What is going on here? thanks!



Reply

Dec 9, 2023 5:17 AM in response to wealthychef

wealthychef wrote:

<string>date > /Users/rcook/test.txt</string>

Launchd isn't running a shell. It is running a single executable the way the C API "execv" might do it. That means you don't have any shell features like search paths or redirection. If you want a shell, you'll have to run it manually with something like:


/bin/sh -c '/bin/date > /tmp/test.txt'


Technically, there are a handful of default behaviours, but I recommend that you don't rely on them. Otherwise, you are likely to get confused about why search paths sometimes work and sometimes don't. Always specific the full path to any executables or files.

plutil ~/Library/LaunchAgents/wealthychef.obsidian-daily.plist
/Users/rcook/Library/LaunchAgents/wealthychef.obsidian-daily.plist: OK

Your config file does have to be a valid property list file. But just because the syntax is valid doesn't mean the task is going to run correctly.

Reply

Dec 9, 2023 9:33 AM in response to etresoft

etresoft wrote:

Launchd isn't running a shell. It is running a single executable the way the C API "execv" might do it. That means you don't have any shell features like search paths or redirection.


…And it means the shebang can matter, as that’s how macOS knows what to launch…



Reply

Dec 9, 2023 7:06 PM in response to etresoft

Thank you again for your comment.

I tried this and now it will not load at all.

Do I need to specify something explictly scheduling?

Is there something wrong with the "utf" part?

Do you know what "launchctl bootstrap" needs? Apparently I need to learn about its syntax to troubleshoot, but I can't figure it out from the man page because I don't know what it means by <domain-target>/<service-id>. I'm so confused by launchd and launchctl! I'm literally just trying to run the "date" command here and output to a file. Note that I have removed the redirection, although you used it in your example, also confusing, LOL.

i am hoping that "StandardErrorPath" and "StandardOutPath" do what it sounds like.


rcook@MacBook-Pro-2021 (~ ): cat /Users/rcook/Library/LaunchAgents/wealthychef.obsidian-daily.plist
<?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>wealthychef.obsidian-daily</string>

    <key>Program</key>
    <string>date</string>

    <key>StandardErrorPath</key>
    <string>/Users/rcook/testing-err.txt</string>

    <key>StandardOutPath</key>
    <string>/Users/rcook/testing-out.txt</string>

</dict>
</plist>
rcook@MacBook-Pro-2021 (~ ): launchctl load /Users/rcook/Library/LaunchAgents/wealthychef.obsidian-daily.plist
Load failed: 5: Input/output error
Try running `launchctl bootstrap` as root for richer errors.
rcook@MacBook-Pro-2021 (~ ): sudo launchctl bootstrap wealthychef.obsidian-daily
Unrecognized target specifier.
Usage: launchctl bootstrap <domain-target> [service-path, service-path2, ...]
<service-target> takes a form of <domain-target>/<service-id>.
Please refer to `man launchctl` for explanation of the <domain-target> specifiers.



Reply

Dec 9, 2023 7:05 PM in response to MrHoffman

No, it means I have to specify the shell explicitly to get shell features, and these would only work inside the script. It means that launchd does not read the shebang, nor the script itself; it is just running executables with arguments, "the way execv might do it." Even though my shell script is executable, etresoft seems to be saying that launchd is not running a shell, so I cannot depend on things like path expansion and redirection.

Reply

Dec 9, 2023 7:14 PM in response to wealthychef

wealthychef wrote:

No, it means I have to specify the shell explicitly to get shell features, and these would only work inside the script. It means that launchd does not read the shebang, nor the script itself; it is just running executables with arguments, "the way execv might do it." Even though my shell script is executable, etresoft seems to be saying that launchd is not running a shell, so I cannot depend on things like path expansion and redirection.


The shebang is how UNIX decides which interpreter to use with a text file.

https://en.wikipedia.org/wiki/Shebang_(Unix)

You’re having trouble launching a script.

Maybe… jusr maybe… it’s because the type of script is getting misinterpreted.

Reply

Dec 10, 2023 8:09 AM in response to wealthychef

wealthychef wrote:

I tried this

No you didn't.

now it will not load at all.

I thought you said it was never working?

Do I need to specify something explictly scheduling?

I don't know what you mean by "explictly scheduling"

Is there something wrong with the "utf" part?

No.

Do you know what "launchctl bootstrap" needs?

Yes.

Apparently I need to learn about its syntax

No, you don't.

I can't figure it out from the man page because I don't know what it means by <domain-target>/<service-id>. I'm so confused by launchd and launchctl!

launchd is a very advanced topic. Your learning approach is haphazard at best.

I'm literally just trying to run the "date" command here and output to a file.

No. You're not. You are trying to schedule a persistent launch daemon, redirecting standard output and standard error, and configure it to run at a specific calendar internal.

Note that I have removed the redirection, although you used it in your example, also confusing, LOL.

That was an example of how to manually run a stand-alone shell and have said shell run a command and redirect the output.

i am hoping that "StandardErrorPath" and "StandardOutPath" do what it sounds like.

They should, but this is an advanced topic. When you try 5 different, new, advanced tasks at once, you are exponentially increasing your problem domain and difficulty.


Here is an example plist file:


<?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>wealthychef.obsidian-daily</string>

<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>/bin/date > /tmp/test.txt</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>10</integer>
</dict>


This XML plist (property list) configuration file does the following:

1) Assigns the label "wealthychef.obsidian-daily" to the task

2) Runs a shell and then runs the "date" command, redirecting its contents to a file

3) Runs the task immediately upon load

4) Runs the task every 10 seconds


You can load the task with the following command:


launchctl load ~/Library/LaunchAgents/wealthychef.obsidian-daily.plist


You can also load the task using the "bootstrap" verb, but I strongly urge you to avoid attempting this, or any of the "modern/non-legacy" interface. As I said before, this is an advanced topic. These modern verbs like "bootstrap", "bootout", and "print" are even more advanced. They are so advanced that I can't even tell you what the command should look like because it requires some knowledge of how you have your configured your system, which I don't have. I can take a guess, and if you've chosen defaults, then my guess would work. But judging by what I've seen so far, there is a good chance that you haven't used those system defaults. The command above is guaranteed to work, as long as you haven't renamed your plist file since your last post.


You should see the "test.txt" appear in the "/tmp" folder immediately. You can run the command:


tail -f /tmp/test.txt


to see it get updated every 10 seconds. Just type control-c to terminate the "tail" command.


You can probably also use the "StandardOutPath" and "StandardErrorPath" keys, but those are going to change the characteristics of how the program runs and updates that file. I didn't include those options, on purpose, in an attempt to simplify the process as much as possible.


Note that I've used the "ProgramArguments" key instead of the "Program" key. The "Program" key is virtually never used or useful.


Once you have successfully run this task, you can then unload it with:


launchctl unload ~/Library/LaunchAgents/wealthychef.obsidian-daily.plist


Yes, yes, yes. You could use the "bootout" verb instead. Same caveats apply as above.


After unloading the task, and verifying that your "tail" command has stopped updating, you can change the "StartInterval" verb to "StartCalendarInterval". This will require more frequent editing, loading, and unloading of the task to test, but that's good practice. You can keep that same "tail" command running. You can even punch in some new lines on the keyboard to make it easier to read and see when it updates.


Reply

Dec 11, 2023 5:30 AM in response to wealthychef

OK, thanks to all who chimed in. I appreciate your patience and persistence.

Here is a working LaunchAgent plist and accompanying shell script that does exactly what I want, which is just to run my awesome little shell script under my user name at a certain time of day.


<?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>wealthychef.obsidian-daily</string>

    <key>ProgramArguments</key>
    <array>
    <string>/opt/local/bin/bash</string>
    <string>-c</string>
    <string>/Users/rcook/bin/obsidian-daily.sh</string>
    </array>
    
    <key>StartCalendarInterval</key>
    <dict>
        <!--Missing arguments are considered wildcards-->
        <key>Hour</key>
        <integer>20</integer>
        <key>Minute</key>
        <integer>14</integer>
    </dict>
    

</dict>
</plist>


Here is the shell script I'm using, with plenty of extra slashes just to put that argument to bed. It works great! The little turd file appears in my home directory, and Obsidian does the right thing with the URL.


#!/////usr/bin/env bash

echo "It is running on $(date)" > /Users/rcook/script-turd.txt
open -a Obsidian 'obsidian://advanced-uri?vault=wealthyvault&daily=true&mode=prepend' 



It's such a relief to have this working. Next test is to change the time to make it run at 4am. From what I understand, when I wake up I will see that it ran at that time, or at some point after that time, when my MacBook wakes up. Good night.

Reply

Why doesn't my launchd plist work as expected? How to troubleshoot?

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.