AppleScript for Beginners VII - Errors

Well, I suppose it finally had to happen. If you have been reading along with these tutorials, you know that we have already had just about as much fun as we are going to have, and it the time has come to start dealing with the dark side of AppleScript. I remember when I first ventured into to the realm of increasingly complex script writing, and the difficulties I had finding out why the heck this script wasn’t working the way I wanted it to work. Of course, I can’t read your mind and have no clue what sort of crazy things you are going to write in your scripts, so we will focus on the general methods to try to solve problems and find the bugs. As usual, let’s start with our final script from last time:Well, I suppose it finally had to happen. If you have been reading along with these tutorials, you know that we have already had just about as much fun as we are going to have, and it the time has come to start dealing with the dark side of AppleScript. I remember when I first ventured into to the realm of increasingly complex script writing, and the difficulties I had finding out why the heck this script wasn’t working the way I wanted it to work. Of course, I can’t read your mind and have no clue what sort of crazy things you are going to write in your scripts, so we will focus on the general methods to try to solve problems and find the bugs. As usual, let’s start with our final script from last time:


tell application "iTunes"
	set five_tracks to {}
	repeat 5 times
		try
			set end of five_tracks to some track of library playlist
		on error errTxt number errNum
			display dialog errTxt & return & errNum
		end try
	end repeat
	try
		play some item of five_tracks
	on error errTxt number errNum
		display dialog errTxt & return & errNum
	end try
end tell

I hope that you had a chance to play with this a little bit, and I REALLY hope that you found the error and discovered the missing word to fix the script. Just in case you have not, please run the script and note that you get the following error:

Can’t get some track of library playlist
-1728

Well, that’s interesting, isn’t it? I mean, sure, it is nearly nonsense, but it is interesting. In fact, there is enough information in this dialog to pinpoint the precise line of AppleScript code where that nasty -1728 error was generated. Let’s start by looking at the script itself. You see that all the code is contained within an iTunes tell block, and then there is a repeat block, that itself contains what is called a try block. The try block is one of the simplest, yet most effective methods of finding, trapping, and getting specific information about errors in your scripts. Basically, whenever an error occurs within a try block, that portion of the script stops, and the script continues to execute, starting right after the end try statement. In our case, the first action we try to do is this line:


set end of five_tracks to some track of library playlist

which then generates the error that alludes to it, by directly stating that the script can’t get some track of library playlist. This tells us where the error is, and the number tells us what the error is. (We will get to the numbers soon enough, hang on for now.) You need to know about the real power of the try block that presents that information to us before we continue. That power is contained in the line that states:


on error errTxt number errNum

This statement activates an error handler that will gather whatever information is available about the error that occurred within the try block. I have never covered handlers in this series, and I have no intention of doing so, since there are already some superb tutorials on this site that do the job already. I highly recommend this one first, and this one second, and finally, this one third. You don’t need to stop now and read them, but bookmark them or print them out for later perusal. If you have made it this far in my series, you are definitely ready to learn the amazing abilities of handlers.

For now, just know that a handler is like a sub-routine. It is sort of a mini-program, or mini-script within your script (or the system) that can be called from your script to do a specific task, after which the script continues on its merry way from the point where you called the handler. In this case, the error handler is part of the system, and you call it by simply stating on error within your try block. Most errors will return an error message in the form of a string, and an error number. The syntax I used above is how you extract those items when your script hits an error; the variable errTxt will hold the error message, and the variable errNum will hold the error number. (You can, of course, make whatever variables you wish for this.) In my case, I then use a display dialog box to show the message and the number:


display dialog errTxt & return & errNum

Although I will not go into any real details here, you can do whatever you want in that on error section, if you believe that it will help you to discover what went wrong with the execution of the script. If you are curious about what sorts of errors are out there, this page outlines many of the AppleScript errors that can pop up, and this page lists a multitude of Apple Event errors that you may see. Neither place has much more information than mere lists, but they are interesting nonetheless.

Heading back to our current script, you see that I have another try block in the latter half of the script, right after the end return. If you run the script, and click OK on each dialog box that comes up while the script is cycling through the repeat loop 5 times, you will get a sixth dialog box that displays this information:

It is the same error number, but with a different message, indicating in this case that we are trying to get some item of an empty list ({}). That would be a bit harder to track down in a long script, so let’s change the on error commands to this:


display dialog errTxt & return & errNum & return & "In the second try block."

This time, when the error is generated, we not only get the information on what the error is, we also get some information about where in the script we need to look to find the error.

I think you have waited long enough to know how to fix this baby if you have not discovered it already. In the first half, the error about Can’t get some track of library playlist should be a giveaway that when we asked iTunes to build a list of 5 tracks, we just said library playlist instead of the correct form first library playlist or library playlist 1, both of which indicate the entire collection of music within iTunes. So, simply by changing that line in our script to read:


set end of five_tracks to some track of library playlist 1

we have fixed the script, and it now functions perfectly, playing one of the items in the list of 5 tracks, no more errors. That is the basis of de-bugging via the try block. Sometimes, one needs to put the entire script within a try block, and hope that the error message it throws will make some sense as to where the problem lies. Other times (although it requires more effort) it is advisable to place a few try blocks in your script, with proper labels in your display dialogs, to isolate just where something is being messed up.

There are other uses for try blocks that you may find useful. For instance, let’s say you have a pathological fascination with numbers, and you cannot sleep until you know how a huge bunch of numbers would correspond to tracks in your iTunes library. Your head is filled with a ton of numbers, and you wonder which tracks they represent in your library, so you put together a stupid script:


set a_bunch_of_numbers to {0, 4, 5, 6, 7, 53, 234, 4, 6, 454, 6, 4, 342, 76, 4569, 11, 53, 66, 77, 88, 44, 34565, 76444}
set track_Names to {}
set err_List to ""
tell application "iTunes"
	repeat with a_number in a_bunch_of_numbers
		try
			set end of track_Names to name of (track a_number of first library playlist) & " - " & a_number & return
		on error
			set err_List to err_List & "There is no track number " & a_number & " in your iTunes library" & return
		end try
	end repeat
	set err_List to err_List & return & return & "But your numbers did correspond to these tracks:" & return & (track_Names as string)
end tell
tell application "TextEdit" to make new document with properties {text:err_List}

In this instance, only two possibilities exist for each number in your list. There is either a track with that number in your library, or this is not. We use the try block to find them (instead of an if/then/else) knowing that if the track exists, its name will be added to the track_Names list. If it does not exist, an error will occur, which will call the on error handler, and thus build the string that will be eventually displayed in a TextEdit document, along with the list of successfully located track names.

As you can see, try blocks are versatile and useful for both locating errors, and just plain sorting through data. As with just about any part of AppleScript, it represents just another way of doing something that certainly could be accomplished with other means (as in our last script), but brings along enough unique tools to make it powerful and useful. And, its usage is only limited by your imagination (or lack thereof). Let’s say that you are POSITIVE that you have written something that will snag every error, and return the information to you within your on error handler, but you are not convinced. Well, genius, why not put a try block within the error handler itself? In the script just above, how about if a particular number doesn’t exist in your iTunes library, so you decide to split the number in half? We know that won’t work, since some of the numbers are odd, but go through the exercise anyway, and look at this iteration:


set a_bunch_of_numbers to {0, 4, 5, 6, 7, 53, 234, 4, 6, 454, 6, 4, 342, 76, 4569, 11, 53, 66, 77, 88, 44, 34565, 76444}
set track_Names to {}
set err_List to ""
tell application "iTunes"
	repeat with a_number in a_bunch_of_numbers
		try
			set end of track_Names to name of (track a_number of first library playlist) & " - " & a_number & return
		on error
			try
				set end of track_Names to name of (track (a_number / 2) of first library playlist) & " - " & a_number & return
			on error
				set err_List to err_List & "There is no track number " & (a_number / 2) & " in your iTunes library" & return
			end try
		end try
	end repeat
	set err_List to err_List & return & return & "But your numbers did correspond to these tracks:" & return & (track_Names as string)
end tell
tell application "TextEdit" to make new document with properties {text:err_List}

We can now catch the errors that are thrown when something goes to the on error handler as well. And yes, you could just keep going and going, nesting more try blocks inside of on error handlers for as long as you could stand to sit at your terminal messing around. The point here is to remember to think creatively in all your scripting. If you are not sure something will work, try it.


tell application "iTunes"
	set five_tracks to {}
	repeat 5 times
		try
			set end of five_tracks to some track of library playlist
		on error errTxt number errNum
			display dialog errTxt & return & errNum
		end try
	end repeat
	try
		play some item of five_tracks
	on error errTxt number errNum
		display dialog errTxt & return & errNum
	end try
end tell

I hope that you had a chance to play with this a little bit, and I REALLY hope that you found the error and discovered the missing word to fix the script. Just in case you have not, please run the script and note that you get the following error:

Can’t get some track of library playlist
-1728

Well, that’s interesting, isn’t it? I mean, sure, it is nearly nonsense, but it is interesting. In fact, there is enough information in this dialog to pinpoint the precise line of AppleScript code where that nasty -1728 error was generated. Let’s start by looking at the script itself. You see that all the code is contained within an iTunes tell block, and then there is a repeat block, that itself contains what is called a try block. The try block is one of the simplest, yet most effective methods of finding, trapping, and getting specific information about errors in your scripts. Basically, whenever an error occurs within a try block, that portion of the script stops, and the script continues to execute, starting right after the end try statement. In our case, the first action we try to do is this line:


set end of five_tracks to some track of library playlist

which then generates the error that alludes to it, by directly stating that the script can’t get some track of library playlist. This tells us where the error is, and the number tells us what the error is. (We will get to the numbers soon enough, hang on for now.) You need to know about the real power of the try block that presents that information to us before we continue. That power is contained in the line that states:


on error errTxt number errNum

This statement activates an error handler that will gather whatever information is available about the error that occurred within the try block. I have never covered handlers in this series, and I have no intention of doing so, since there are already some superb tutorials on this site that do the job already. I highly recommend this one first, and this one second, and finally, this one third. You don’t need to stop now and read them, but bookmark them or print them out for later perusal. If you have made it this far in my series, you are definitely ready to learn the amazing abilities of handlers.

For now, just know that a handler is like a sub-routine. It is sort of a mini-program, or mini-script within your script (or the system) that can be called from your script to do a specific task, after which the script continues on its merry way from the point where you called the handler. In this case, the error handler is part of the system, and you call it by simply stating on error within your try block. Most errors will return an error message in the form of a string, and an error number. The syntax I used above is how you extract those items when your script hits an error; the variable errTxt will hold the error message, and the variable errNum will hold the error number. (You can, of course, make whatever variables you wish for this.) In my case, I then use a display dialog box to show the message and the number:


display dialog errTxt & return & errNum

Although I will not go into any real details here, you can do whatever you want in that on error section, if you believe that it will help you to discover what went wrong with the execution of the script. If you are curious about what sorts of errors are out there, this page outlines many of the AppleScript errors that can pop up, and this page lists a multitude of Apple Event errors that you may see. Neither place has much more information than mere lists, but they are interesting nonetheless.

Heading back to our current script, you see that I have another try block in the latter half of the script, right after the end return. If you run the script, and click OK on each dialog box that comes up while the script is cycling through the repeat loop 5 times, you will get a sixth dialog box that displays this information:

It is the same error number, but with a different message, indicating in this case that we are trying to get some item of an empty list ({}). That would be a bit harder to track down in a long script, so let’s change the on error commands to this:


display dialog errTxt & return & errNum & return & "In the second try block."

This time, when the error is generated, we not only get the information on what the error is, we also get some information about where in the script we need to look to find the error.

I think you have waited long enough to know how to fix this baby if you have not discovered it already. In the first half, the error about Can’t get some track of library playlist should be a giveaway that when we asked iTunes to build a list of 5 tracks, we just said library playlist instead of the correct form first library playlist or library playlist 1, both of which indicate the entire collection of music within iTunes. So, simply by changing that line in our script to read:


set end of five_tracks to some track of library playlist 1

we have fixed the script, and it now functions perfectly, playing one of the items in the list of 5 tracks, no more errors. That is the basis of de-bugging via the try block. Sometimes, one needs to put the entire script within a try block, and hope that the error message it throws will make some sense as to where the problem lies. Other times (although it requires more effort) it is advisable to place a few try blocks in your script, with proper labels in your display dialogs, to isolate just where something is being messed up.

There are other uses for try blocks that you may find useful. For instance, let’s say you have a pathological fascination with numbers, and you cannot sleep until you know how a huge bunch of numbers would correspond to tracks in your iTunes library. Your head is filled with a ton of numbers, and you wonder which tracks they represent in your library, so you put together a stupid script:


set a_bunch_of_numbers to {0, 4, 5, 6, 7, 53, 234, 4, 6, 454, 6, 4, 342, 76, 4569, 11, 53, 66, 77, 88, 44, 34565, 76444}
set track_Names to {}
set err_List to ""
tell application "iTunes"
	repeat with a_number in a_bunch_of_numbers
		try
			set end of track_Names to name of (track a_number of first library playlist) & " - " & a_number & return
		on error
			set err_List to err_List & "There is no track number " & a_number & " in your iTunes library" & return
		end try
	end repeat
	set err_List to err_List & return & return & "But your numbers did correspond to these tracks:" & return & (track_Names as string)
end tell
tell application "TextEdit" to make new document with properties {text:err_List}

In this instance, only two possibilities exist for each number in your list. There is either a track with that number in your library, or this is not. We use the try block to find them (instead of an if/then/else) knowing that if the track exists, its name will be added to the track_Names list. If it does not exist, an error will occur, which will call the on error handler, and thus build the string that will be eventually displayed in a TextEdit document, along with the list of successfully located track names.

As you can see, try blocks are versatile and useful for both locating errors, and just plain sorting through data. As with just about any part of AppleScript, it represents just another way of doing something that certainly could be accomplished with other means (as in our last script), but brings along enough unique tools to make it powerful and useful. And, its usage is only limited by your imagination (or lack thereof). Let’s say that you are POSITIVE that you have written something that will snag every error, and return the information to you within your on error handler, but you are not convinced. Well, genius, why not put a try block within the error handler itself? In the script just above, how about if a particular number doesn’t exist in your iTunes library, so you decide to split the number in half? We know that won’t work, since some of the numbers are odd, but go through the exercise anyway, and look at this iteration:


set a_bunch_of_numbers to {0, 4, 5, 6, 7, 53, 234, 4, 6, 454, 6, 4, 342, 76, 4569, 11, 53, 66, 77, 88, 44, 34565, 76444}
set track_Names to {}
set err_List to ""
tell application "iTunes"
	repeat with a_number in a_bunch_of_numbers
		try
			set end of track_Names to name of (track a_number of first library playlist) & " - " & a_number & return
		on error
			try
				set end of track_Names to name of (track (a_number / 2) of first library playlist) & " - " & a_number & return
			on error
				set err_List to err_List & "There is no track number " & (a_number / 2) & " in your iTunes library" & return
			end try
		end try
	end repeat
	set err_List to err_List & return & return & "But your numbers did correspond to these tracks:" & return & (track_Names as string)
end tell
tell application "TextEdit" to make new document with properties {text:err_List}

We can now catch the errors that are thrown when something goes to the on error handler as well. And yes, you could just keep going and going, nesting more try blocks inside of on error handlers for as long as you could stand to sit at your terminal messing around. The point here is to remember to think creatively in all your scripting. If you are not sure something will work, try it.

Hi there,

Thank you for this article. I’m doing my best to learn from it.

The links you provided to good articles covering handlers are not working (at least for me). Would you please check that these documents haven’t moved somewhere else?

Thanks,

Patrick

I hope this isn’t appropriate or anything, but a lot of scripting deals with either handling errors, or figuring out what is going wrong. Now, figuring out what is going wrong, is reasonably easily easy when you are running your scripts from within AppleScript Editor, or Script Debugger, or XCode for that matter.

The approach above with dialog boxes, is of course the first line of deducing what is going wrong, when you can’t run your script in an editor. Maybe you are creating an applet, or an automator service, and the error doesn’t appear every time. Then what?

Well, I have a handler here, below, that I didn’t write, but it is tremendously useful. It takes to parameters;

  • A string containing the error condition: e & n inside on error e number n is perfectly good.

  • The name of a logfile, this logfile can be obtained from the sidebar of the console app, under ~/Library/logs

It is a good idea to allways put “my” in front of the handler, as you then assure that your script will find the handler, provided it is located within the script that has the handler that calls it.


try
	tell application "Finder"
		if exists folder "Desk top" of home then
			set tvar to true
		else
			set tvar to false
		end if
	end tell
on error e number n
	my logit("Can't find desktop:" & e & " " & n, "errlogdemo")
end try

to logit(log_string, log_file)
	do shell script ¬
		"echo `date '+%Y-%m-%d %T: '`\"" & log_string & ¬
		"\" >> $HOME/Library/Logs/" & log_file & ".log"
end logit

I don’t agree, it’s a better idea to use application tell blocks only for code which uses the respective application terminology, for example


tell application "Finder" to set desktopExists to exists desktop
if desktopExists then
	doSomething()
else
	doSomethingElse()
end if


The rule to use the keyword my in front of a handler is very simple:
It’s only required inside any tell block

Hello.

:slight_smile: The example, was contrived, as it was just a usage example, how to use the handler.

The reason for my recommendation of prepending with “my”, is that the milage varies, so when you are swimming around in new terms, and really need to put an effort into making a script work, then it is better to have some extranous “my”'s and a script that doesn’t fail when calling the log handler inside a tell block,

And it is not only useful for tracking errors, you can also use the handler for following the program logic, It is always good to see what actually happens.

Hi Patrick,
Yes, they are from a previous incarnation of Macscripter. At the end of those URLs is a number. The number is still correct, but the rest of the URL has changed. Just open a page for another thread, and replace the number at the end of its URL with the number from the broken link.