I wrote an AppleScript to synch my Reminders (via export to JSON). It runs great... from the Script Editor. As soon as I tried to run it on the command line via osascript
, I discovered it hits a wall when it tries to access reminders. After maybe a minute and a half, I get this error:
/Users/robleach/Temporary/synchRemindersTest.scpt: execution error: Reminders got an error: AppleEvent timed out. (-1712)
I also noted these errors in the console:
error 19:33:49.628309-0400 tccd Refusing client without path (from responsibility_get_responsible_audit_token_for_audit_token) PID[1422]: (#3) No such process
error 19:33:49.628370-0400 tccd Refusing TCCAccessRequest for service kTCCServiceReminders from invalid client with pid 1422
Assuming it was a permissions issue, I looked in System Preferences>Security & Privacy>Reminders, and noted that osascript
is not there, nor do I have ± buttons to add it, even after authenticating.
I wondered whether saving the script as an app would prompt the Security stuff to prompt me to enable it - and that I could cron the opening of that app, but if I do that, I won't be able to pass arguments to the script (or at least, I don't know how to do that). Plus, I'd rather everything happen in the background, without a dock icon or anything (aside from the Reminders app needing to be open).
I wrote a toy example of the script that produces the timeout error:
TOY 1
tell application "Reminders"
return (properties of every reminder whose completed is false)
end tell
And I call it like this:
> osascript /Users/robleach/Temporary/synchRemindersTest.scpt
Is there a way to allow the osascript run of the script to be allowed access to the reminders? Could I like code-sign the command line executable or something? If I were to write this in another language, would it have the same issue?
I'm running Catalina 10.15.7.
UPDATE 1
I dug some more into the console. There are number of other, potentially relevant errors. I think it actually is a timeout. When I run it in Script Editor, it runs in about 40 seconds, but it times out when I run it via osascript (at maybe around a minute and a half).
However, I recalled that I have another script on a cron job that accesses reminders, and I did not remember it having an issue. So I tested it, and for whatever reason, it executes a very similar command, but succeeds. And it runs much faster in the Editor than it does on the command line from osascript. I pulled that one line out of that script that succeeds and wrapped it in a toy script:
TOY 2
tell application "Reminders"
set theList to "ToDo Home Recurring"
set namesDates to {name, due date} of (every reminder in list theList whose completed is false)
display dialog "Got " & (count of namesDates) & " reminder names & dates"
end tell
and it failed via osascript with the same timeout error. Then I pulled out another line and added it to the toy:
TOY 3
tell application "Reminders"
set theList to "ToDo Home Recurring"
show list theList
delay 0.25
set namesDates to {name, due date} of (every reminder in list theList whose completed is false)
tell application "System Events" to display dialog "Got " & (count of namesDates) & " reminder names & dates"
end tell
...it succeeds. So I don't think this is a permissions issue any longer. This feels like it maybe has to do with how efficiently osascript accesses Reminders.
I also started noticing that my first toy example above sometimes fails when run from the script editor. I kept retrying it to get the running time I pasted above and I started to sense a pattern. I think that when I select a new list to view in the Reminders GUI (it doesn't matter which one), then run the script (from the editor), it works. But if I don't select a new list to view and run the script from the editor again, it fails with that timeout.
...but that seems nuts. Can anyone explain what's going on here?
NOTE: The script I'm writing is actually an AppleScript re-write of a Siri Shortcut I wrote (that runs reliably in around 25ish seconds). Since I wanted to automate it and run it more than once a day, I decided to use AppleScript.
UPDATE 2
I tried the answer suggested by @Robert Kniazidis.
TOY 4 (modification of TOY 1)
with timeout of 3600 seconds
tell application "Reminders"
set allRems to (properties of every reminder whose completed is false)
display dialog "Got " & (count of allRems) & " reminders"
end tell
end timeout
...and kept a closer watch on the console.
Attempt 1 (TOY 4)
I ran TOY 4, starting at 7:25:24, for 10 minutes, then control-c'd it. I immediately saw numerous errors in the console. I searched the console for "Reminders" and here's what I go during the run.
Attempt 2 (TOY 4)
Then, given my insights regarding my annecdotal successes when I click a list name in the Reminders GUI, I tried clicking on a random list and ran TOY 4 again immediately after. I started TOY 4 at 7:38:23. At 7:44:22, it succeeded! That's about 6 minutes!
The console had many fewer messages in it, none of them marked as error. For comparison's sake, here are the console results from searching for "Reminders".
Discussion
I have revised my theory about what's happening. Given the console messages, I infer that when you run by osascript from the command line, the script is identified as "indirect access", and as such, is subject to a higher level of security-scrutiny, thus is takes much longer to execute. Perhaps when I "click in the GUI" (or even via AppleScript, show list theList
), the security concern is still considered "indirect", but is not totally unknown to the user because the GUI is changing, so is subject to somewhat less scrutiny, and thus takes 6 minutes as opposed to somewhere over 10 minutes.
If that's true, it is interesting to note that that lesser level of scrutiny is applied even if the Reminders GUI is on a different desktop (as has been the case in my tests*).
UPDATE 3
I tried ad-hoc code-signing this morning with:
codesign --force -v -s - synchRemindersTest.app/Contents/Info.plist synchRemindersTest.app/Contents/PkgInfo synchRemindersTest.app/Contents/Resources/applet.rsrc synchRemindersTest.app/Contents/Resources/Scripts/main.scpt synchRemindersTest.app/Contents/Resources/applet.icns
...and ran the app again, which was a version of TOY 1. Still got the timeout error. I was hoping it would take 40s, like when run from the script editor. When I get time, I'll try it again but manually select a list in the Reminders GUI.
UPDATE 4
I ran the same toy as in update 3 again just now. For the 2 minutes it ran before the timeout, the console was filled with 52,349 lines of mostly this, repeating over and over, which is only the portion of that timespan that matched the search term tccd
.
I also have noted that the same unmodified script, run at different times, will succeed in some runs and fail on others. Such as:
TOY 5 (synchRemindersTest5.scpt)
with timeout of 600 seconds
tell application "Reminders"
show list "ToDo Home"
set startt to (get current date)
set allRems to (properties of every reminder whose completed is false)
set endt to (get current date)
set dur to (endt - startt)
set msg to "Got " & (count of allRems) & " reminders in " & dur & " seconds"
tell application "System Events" to display dialog msg giving up after 5
return msg
end tell
end timeout
I ran it repeatedly yesterday, successfully, but I get the timeout when I run it today:
[Jun 08 22:59:51]:~/GoogleDrive/Scripts>osascript synchRemindersTest5.scpt
Got 166 reminders in 287 seconds
[Jun 08 23:06:17]:~/GoogleDrive/Scripts>osascript synchRemindersTest5.scpt
Got 166 reminders in 291 seconds
[Jun 08 23:11:45]:~/GoogleDrive/Scripts>osascript synchRemindersTest5.scpt
Got 166 reminders in 293 seconds
[Jun 08 23:17:46]:~/GoogleDrive/Scripts>osascript synchRemindersTest5.scpt
Got 166 reminders in 300 seconds
[Jun 09 8:23:28]:~/GoogleDrive/Scripts>osascript synchRemindersTest5.scpt
synchRemindersTest5.scpt: execution error: Reminders got an error: AppleEvent timed out. (-1712)
Footnotes
* I've been testing my scripts intentionally with the Reminders app on a different desktop because I have noted in my efforts that GUI scripting is always faster than accessing via the Reminders dictionary. So I wrote 2 methods: GUI and Reminders Dict. The GUI one will run if the open Reminders app is on the desktop (which I keep tucked under the dock). If we're watching Netflix full screen, I have a try/catch to use the slower Reminders Dict access method when the GUI is, as in that case, on a different desktop.