Let’s consider the problem of how to handle exceptions? Assume we have already chosen to go beyond the “method” of simply ignoring them by placing a Set Error Capture[ On ] step at the beginning of a script. Shouldn't we then just write code like this?
Well, I think that though the code above is correct, it still has a problem: it mixes the exception part with the main functionality. It's difficult to follow such a code. Imagine there's three error-checking conditions in a script and you're reading it after a while; you'll have to open three script steps and read their formulas to understand what's going on.
Though you cannot avoid the mix, it worth trying to clearly separate the part that handles exceptions from the rest of your code. After all, exception handling requires very limited functionality: you need to throw an exception, watch for it, and handle it somehow.
I'm going to describe a system I use. The description is fairly long, but actually the system is very simple and takes a couple of fields, a couple of scripts and a handful of very simple custom functions.
The Exception field
The exception state is stored in a global text field Exception. In pre-7 FileMaker applications I placed the field in the “main” or “system” file with one record only; in FileMaker 7 and 8 I store this field in the “main” or “system” table, which also has only one record. It’s possible to use a variable in v8, but since the field is also used as a key for a relationship (see below), a variable would actually complicate the matter.
Checking for an exception
If the field is empty or is equal to zero it means there's no exception; if it contains a non-zero value it's an exception. This is a text field and it can contain both text and numeric values.
In pre-7 FileMaker applications I checked this by:
In FileMaker 7 and 8 I typically write a custom function ERROR:
Depending on what I'm to check I write either
or
The only purpose of this custom function is read- and writability.
Throwing exceptions
Throwing custom exceptions
To throw an exception you have to set the Exception field to some value. For example, if a user will run a script that has to check various conditions, the script is written this way:
Here you can already see some advantages to the described approach. The conditions that are checked and set by the Set Field step typically look something like this:
This single step can check many conditions at once and produce a meaningful description for every particular problem (later the message will be shown to the user). What's good here:
In spite of how detailed the exception checking is and how verbose the error messages are, all it takes is one script step.
It scales perfectly. If you need to add, or remove, or rearrange, or make more sophisticated checks you still have to modify one script step. The rest of the code will function as it did before.
Intercepting exceptions thrown by FileMaker
Quite often you need to intercept a standard FileMaker exception that may be thrown by a script step. To separate this from the main functionality I typically write a few scripts that capture exceptions thrown by certain FileMaker commands, like this:
or this:
As you can see the scripts are quite alike; only the main command differs. These scripts are used and errors are checked in exactly the the same way:
Handling exceptions
The pattern we have so far has two basic steps:
Check whether there's an exception.
If no, continue with the main functionality.
Handling exceptions means doing something for “if yes” clause. There are several possible ways to react to an exception: warn a user, recover (i.e. do something extra to main functionality), or ignore it.
Note that it’s not possible to encounter more than one exception, because as soon as the first exception is encountered, it is (must be) handled. If the exception is fatal, the script warns the user and quits, so it doesn’t meet any more exceptions on its way out. If the exception is non-fatal, than it is dealt with somehow and cleared, so when the script continues, there is no exception anymore.
Displaying custom error messages
This is the typical reaction to an exception. In most scripts we write the only thing we want to do when we encounter an exception is to inform the user about it and let the person correct the cause:
If yes, display an error message and quit.
The natural time to display an error message seems to be at the end of a script. Not just at the end of every script, of course; scripts are often called from each other, yet we only want to handle each exception once. This means we need to ensure that the end of a script is really “The End” and no code runs after this point.
To achieve this I divide all scripts into two kinds:
Button scripts. These scripts are never called from other scripts; when they end, they really end. These scripts scripts can call other “non-button” scripts. Button scripts are typically attached to buttons or menu items and run by users.
Subscripts. These scripts are called from other scripts; a user cannot call them directly. Subscripts can call other subscripts. Subscripts have a dash prefix, like this: - Subscript.
A similar approach could probably be applied to other non-FileMaker agents, like Apple Script or plug-ins, but don't have enough experience with this to establish a pattern yet.
So buttons scripts are scripts that really end. At the end of every button script I place a standard command that does the default exception handling: warning a user. Such a standard end script quite naturally suggests a standard start command, so actually a minimal button script looks like this:
and a minimal sample of a button script with error handling will be this:
What do the standard scripts do?
- Begin script clears the Exception field; there's no exception when a script starts (a button is pressed or a menu item is selected).

- End script checks whether there's an exception and if yes, displays an error message. To do this, it calls a script that displays an error message.

Why modularize it further? Why not to put all the code in the - End script? Because sometimes you write critical scripts that handle re-logins to an application and these scripts simply cannot continue if they got an exception. In this case the same error message script is called in the middle of a script, not at the end.
The script that displays the error message is - Display System::Exception and looks like this:
The script simply shows the contents of the Exception field as a custom dialog. As you saw above the field at this point contains an error message, which can be as specific or detailed, as FileMaker calculations allow; yet it still takes only a few script steps to put all this to work.
Displaying standard FileMaker error messages
The actual script I use is a bit more complex, because it also handles FileMaker messages. As you saw above I intercept FileMaker numeric error codes using the Get( LastError ) function. I also have copied the table from the description of this function in FileMaker help, massaged it a bit and converted into a data table with the following fields:
In the applications I write I make a relationship from System::Exception to Exception::Number:
and add a global text field Exception Description that auto-enters a value from Exception::Description overwriting the current value.
I made the field global because I want it to be accessible in all tables without having to link these tables to the System table. An unstored calculation would be unaccessible and a global calculation doesn't update when Exception changes.
To display these messages I add an Else If condition to the - Display System::Exception:
By the way, such a table of standard exceptions scales perfectly. For example, it can contain exception descriptions for different languages and every user can get messages in his or her preferred language. It's also easy to extend the library with error messages generated by plug-ins.
Mac OS X tip: if you develop several applications at once you might want to share the file among all the applications. Making several copies isn't good, because such a resource benefits from being centralized and having all changes automatically propagated to all the points of use. Using aliases isn't convenient either because when you archive the folder, the archive won't include the original file, only the alias. Fortunately, Mac OS X can really place the same file in different folders using “hard links”:
Beeping
If an exception arises from a user slip that is relatively minor and clearly obvious from the context, it may be enough to simply beep at it. To use beeps I throw the “Beep” exception:
with a simple handler:
Catching exceptions
Catching exceptions is another way to handle them:
If yes, do something else.
Typically you don't catch any exception, because the only universal handler for any exception is to display an error message and we already have this one. Instead you check whether the exception is of expected type. And most the exceptions you want to catch are standard FileMaker exceptions, like “401, nothing found” (the favorite, I believe), or “301, record in use”.
Technically this is very simple:
but to make code more readable I always write constant custom functions for this. For example, I have a custom function FOUND NOTHING that returns 401 and write the condition above so:
I don't create such functions for all possible exceptions, but simply add them as I need them. Typically I have may be ten such functions in an application.
When the exception is caught and you did something extra to recover from it, then the exception must be eliminated:
or, if you use constant custom functions to represent nothing:
By the way, sometimes I re-use FileMaker exceptions, that is I throw them myself. The favorite is USER CANCELED (returns 1). For example, if I'm to make a dialog with a “Cancel” button, the script typically looks like:
Ignoring exceptions
Some exceptions can be skipped.
If yes, ignore it.
For example, I want to catch find script exceptions, but I'm pretty happy with the empty found set. This means I need to ignore this particular exception, but not others. To do this I write the following:
The calculation is:
This step clears the field if the exception is FOUND NOTHING (401), but preserves all other exceptions.
Another example of an exception that is always ignored is USER CANCELED (1). This is a default handler for this exception, so I have put it in the - Handle System::Exception script:
Handling standard exceptions
Some exceptions are often checked from several places. For example, a user may want to delete a record from its own table or from a portal it is displayed through; many scripts that change record data need first to make sure the record can be edited, i.e. it isn't “locked” or something like that.
To handle such exceptions I often turn them into fields. I add a calculated text field Edit Exception containing all the conditions I need to check:
and when I'm to check whether a record is editable, I simply
The same for delete exception; though it isn't used that often, it benefits from being centralized and having meaningful error descriptions.
Unexplored: I haven't tried it, but may be it worth using
when defining “limited” editing privilege.
Technorati tags: FileMaker, FileMaker 7, FileMaker 8.
This seems very smart and elegant Mikhail, also somewhat complicated to learn whithout a sample file.
I would love to see your approach in a sample file ?
Regards,
Willem-Jan
Posted by: Willem-Jan Kempen | January 23, 2006 at 11:47 AM
A sample file for this technique has to be a little application that does something useful; such an application will have lots of places to handle exceptions. It also must be open source to be explored. Hmm. I'll see what I can do :)
Posted by: Mikhail Edoshin | January 24, 2006 at 09:10 PM