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.