What is Best Practice for Handling Script Errors in Execute Script Action?

###What is Best Practice for Handling Script Errors in Execute Script Action?

Well, it is somewhat embarrassing to be asking this question after the many, many scripts I have written for the KM Execute Script Action. But recently I have found a need to handle ALL script errors properly in the KM macro.

Here was my first surprise:

KM provides a token called %ActionResult%, which does NOT seem to trap or identify script errors. It seems to always return "OK" even if the script has an error. See KM Wiki: ActionResult Token

I searched the KM Wiki, but could NOT find any info on handling script errors. See User Manual Page on Scripting.

@peternlewis, what am I missing? Is there some option, or better way to cause the "Execute Script" Action to return a non "OK" response when there is a script error?

Below is an example macro that demos the issue. Run this macro, but click on the "Cancel" button to the Choose Folder dialog.

Note the results display below: %ActionResult% returns "OK", but the KM Variable set by the Action clearly shows an ERROR.

So, this begs the question: What is the best way, in general, to trap for script errors, and cancel the Macro is there is an error?

I have started developing my own KM script error handling process, but I'll post that in a reply below.

If you have already developed a script error handling process, or have ideas for one, please reply.


###MACRO: Script ERROR Handling TEST

Script ERROR Handling TEST.kmmacros (6.3 KB)


###Results if User Cancels Choose Folder Script

I’ve developed what I think is a pretty good process, that I use for almost all scripts, whether they’re JXA or AppleScript.

In general, the only thing I “return” from my scripts is either “OK”, or an error message. All values that the script returns are returned via KM variables.

So I run a script, saving the result to a variable. If the variable is not “OK”, I display the variable and cancel the macro.

I also make sure to wrap the entire script in a try/catch. If I catch an error, I “return” the text of the error. Why would I do this, you ask? The first reason is the answer to your “cancel the dialog” question. I still only need to check the result variable.

But also, if the script is a JXA script, and it’s being run under Yosemite, it sometimes returns funky warnings that are pretty useless. So when I run the script, I turn off the option to return errors, and that gets rid of the warnings. But since I capture all runtime exceptions inside the script, I can still return them, and they don’t get lost.

I hope that makes sense.

Yep, makes perfect sense, and is exactly where I was headed. :wink:
But I stopped because I thought it very strange the the KM token %ActionResult% does NOT capture script errors.

It would provide more flexibility, especially for non-scripting users of my macros, if the script results could be returned using the KM Action options, and then just check the %ActionResult% for non "OK".

So, unless @peternlewis can show us a better way, I agree with your approach to always:

  1. Return either "OK" or the error msg in the Action results variable
  2. Put all script code in Try/Catch blocks.

So here's a thought. Could we (that would be you) design a KM plugin that supports:

  • Selection of scripting language
  • Enter text of script, or path to script file
  • Standard error handling (i.e. cancel macro if script error)

####OR

Would it be better to implement in a sub-macro?

Just had another idea.

What if the script returned "[OK]" & the actual results?

if results != "^[OK]" then error/cancel macro
else
  results = results(5)  // all chars after "[OK]"
end if

with the error msg being in the returned variable.

Your thoughts?

Here is an example of my new script error handler.

###My Process (for now):

SCRIPT RETURNS EITHER to a KM Variable:
• If NO Errors: "[OK]" & actual results
• If Error: Script Name & error message

AFTER EXECUTE SCRIPT Completes:
• Test for "[OK]" at start of results
• If found, remove "[OK]" from results variable

  • If NOT found, show error msg and cancel macro

@DanThomas's process will work just as well:

  • Script sets all needed KM Variables
  • Script Returns:
    • "OK" if no errors
    • Error message if script error

2016-08-10 19:07 CT

###MACRO: Script ERROR Handling TEST Ver 3.0

Script ERROR Handling TEST Ver 3.0.kmmacros (5.4 KB)


This is same as Ver 2.0, except is implemented with the below sub-macro.


###MACRO: [KM] Script Error Handler [Sub-Macro]

[KM] Script Error Handler [Sub-Macro].kmmacros (6.7 KB)


###Main Macro


###Sub-Macro


###Results if Error Was Found

Just posted my released sub-macro:

I suppose it could return OK and also the result, but I like it better with one thing for one purpose, if you know what I mean. But I don’t think it’s a big deal either way.

As for handling errors, I prefer to have the code all self-contained, rather than force the user to install something else. I mean, it’s pretty easy to have a standard set of actions to insert, either using a named clipboard, or some other way (hint at a future macro from me, if I ever quit getting sidetracked).

I agree in general, but in practice with KM I'm not sure how to avoid it.
With your simpler approach, there still needs to be another Action to test for error, and act on it.
You could write that Action every time, or call a sub-macro -- your choice.

But I'm definitely open to other approaches that simplify things.

If the user is willing to install a sub-macro, then this looks pretty simple to me:

If you would be willing to mod your standard to return "[OK]" instead of just "OK", then your scripts would work just fine with my sub-macro. Your choice, of course.

Well, I was trying to avoid going into details, because I have something in the works, but here's what I actually do:

Through some mechanism which will be unnamed as of yet, I launch a macro that presents this prompt:

I enter the variable name, like, say "klamResult", press Enter, and this is inserted into the macro I'm working on:

Which, when expanded, looks like this:

Not currently, no.

Thanks for the confirmation.

It's nice to know that a custom handler, like Dan and I have developed, was not a waste.

May I make a feature request? If the Execute Script has an error, then the %ActionResult% should report the error message, not "OK".

Looks very similar to my sub-macro:

Great minds, and all that. :slight_smile:

Well, the saga continues. :smile:

Today I discovered a macro I had written some time ago that used a keyword in the script return variable to detect an error: "[ERROR]".

So, instead of checking for no errors (ala "OK"), this checks specifically for an error. In any case it does NOT change the return variable.

Obviously, I like both ways. But, after thinking about it, and using it some, I'm starting to like the check for "[ERROR]" better. This also allows another error return of "[USER_CANCELED]", which may need to be handled different, especially in reporting the results to the user.

So here is yet another alternated method of script error handling in a KM Execute Script Action.

####I'd love feedback from all interested as to which method you like best.


###MACRO: Script ERROR Handling TEST Ver 4.0

Script ERROR Handling TEST Ver 4.0.kmmacros (11 KB)


###The Script

### RETURNS a POSIX path like "/Users/Shared/Dropbox/" ###
property propScriptName : "Choose Folder & Return POSIX Path"

set promptStr to "Choose a FOLDER"
try
  set x to 0
  --set y to 1/x
  
  set folderPath to POSIX path of (choose folder with prompt promptStr)
on error errMsg number errNum
  
  if errNum = -128 then ## User Canceled
    set folderPath to "[USER_CANCELED]" & return & return & "SCRIPT: " & propScriptName
  else
    set folderPath to "[ERROR]" & return & return & "SCRIPT: " & propScriptName & return & "Number: " & errNum & return & errMsg
  end if
  
  
end try

return folderPath

###Example Results

If you’re going to return results, as opposed to setting KM variables, then this method is OK as long as an empty result is not valid. But if there’s ever a time when an empty result is valid, then you can’t use this method, because there’s no way to guarantee that the script actually ran.

Regardless, I don’t see a problem with returning things like “user canceled” in the result - that’s cool. But, in your example, you combine them all into one general-purpose error message, so I don’t see the point. :wink:

By the way, you should verify that whatever method you choose (in JXA scripts) works correctly in that Yosemite environment that causes the strange warnings.

And just to verify that I’m thinking correctly, we should ALWAYS (in JXA) wrap everything in a try/catch, then “return” the error message, rather than let the error bubble up via stderror, because we generally turn off “return errors” (because of the Yosemite issue), right?

All good points.

My example is just that, an example, not a finished product.

Actually, I think that if the script did not return "[ERROR]", and KM did not throw any errors, then it is a safe bet that it did actually run. Do you see some other issue?

It is easy enough to change the IF/THEN to a SWITCH/CASE, and handle each return type separately. That would probably be best in a finished product.

Well, there are two points:

  1. The error msg the user sees is different
  2. As I just said above, use SWITCH/CASE if you want to handle the different returns.

This is easily handled by:

  1. Uncheck "Include Errors" in the Execute Script action
  2. Use try/catch in the script

Yes, just like I did in my AppleScript example, and in the JXA example before that.

All said, I think we are in general agreement.

However, my main question was checking for "[ERROR]" vs "[OK]".
Do you have any thoughts on that?

Here's an updated version of my script error handler, that uses the KM Action Switch/Case to handle the various types of return from the script.

All constructive criticism and suggestions for improvement very welcome.


###MACRO: Script ERROR Handling TEST Ver 4.1

Script ERROR Handling TEST Ver 4.1.kmmacros (17 KB)

One of the cardinal rules to being a good developer is to understand that you can't know everything. I have no idea if it could return nothing, and have not run successfully. But I would never make an assumption one something like this that I don't need to. And I don't need to. If it doesn't return "OK", something failed. End of story.

One of the reasons I am adamant about things like this, is because I want to get it right and never think about it again. I want to know I've accounted for everything I can, even the unforeseen, if possible. I want the code to tell me when there's a problem. I don't want to have to guess about it.

But as I wrote to someone else today, we're not creating mission-critical software. I know you, of all people, know all about that. :slight_smile: So in the end, it's all good. Do what feels right to you. If it ends up not working, you've got other options.

OK. I can't argue with that. So, bottom line, is that all scripts should return something, with "OK" being the minimum if the script completely successfully (for macros that use the results in further Actions). Unless, of course, the point of the script it to set the Clipboard, or just display results.

So, to combine our approaches, we could do this:

Test for "OK", "[1]", "[2]", "[3]", or other text.
If we get nothing, i.e. a blank result, then treat that as an error.
(I have used "^" to denote at the start of the return string.)

Agreed?


  1. OK ↩︎

  2. ERROR ↩︎

  3. USER_CANCELED ↩︎

Well, I’m good with that, although I’d only check for OK - everything else is an error as far as I’m concerned, with the possible exception of things that actually can be canceled, which aren’t that many.

But if you go this way, it means you don’t return actual values via “return”. Personally, I’m good with that, but I didn’t think you were, so I want to remind you.

I’m sure most people would consider that overkill. Not me, obviously, but I’m used to people looking at me funny. Maybe you should think on it for a day or two, before you sit at the weird kids’ table. :slight_smile:

By the way, I had a personal chuckle today. I was talking about how we weren’t actually writing mission-critical apps, and that made me think of you. That phrase probably means more to you than to me!