Error handling is a big deal; and this website could stand to have at least one, if not several, pages dealing with it. The following collected ideas relate to error behavior in scripting, but it may become appropriate to split recommendations into separate pages for Standards, Best Practices, and Accepted Techniques based on the discussion of these issues. Ideas may be added or removed in the process. Please make suggestions.
"Error handling" is an umbrella term for how an application programmatically responds to errors. It's also too vague for a comprehensive discussion of error handling behaviors. A script can react to an error in any of several ways, and may respond to any one error with more than one of these behaviors:
It is also useful to distinguish between an error and a fault in execution of a script. A fault is something that went wrong; an error is what a script detects when a fault happens. For example:
Resolve an error, or report it, but not both. A script that encountered and resolved an error did manage to complete its expected task, so reporting the error is likely to lead to problems when a user or calling script takes action assuming the task was not completed. So either resolve, or abort and report. This corresponds to the strong exception guarantee.
Use an error dictionary custom function. Some functions already exist that retrieve human-readable descriptions of FileMaker-generated error codes. (For example, ErrorString.) If you use one of these functions, remember to augment it with any additional custom error codes you use in your solutions.
When reasonable, respect the error capture state that was active when a script was initially called, i.e., don't present errors to users if a parent script is capturing errors, and therefore assuming responsibility for reporting errors to users. A parent script has broader context, and is better positioned to provide a helpful response to an unresolved error. This idea might be extended by differentiating script roles along similar lines to the model-view-controller model.
# Beginning of script Set Variable [$errorCaptureOn; Value:Get ( ErrorCaptureState )] Set Error Capture [On] ... If [$error and not $errorCaptureOn] # Report error to user Show Custom Dialog [Get ( ScriptName ); "Encountered error: " & $error] End If ... # End of script If [not $errorCaptureOn] Set Error Capture [Off] End If
When reporting an error to a user, include steps to take to address the problem. For example, a "Free Lunch" script may ask the user to pre-pay their FileMaker developer $1 million before delivery of free lunches can begin.
11 Comments
Matt Petrowsky
Nice start Jeremy. If you've looked at my custom functions in the repository then you may be able to decipher how I'm handling errors - or not (it's a bit of a mishmash of evolution).
I use more of the try/catch that java uses and throw exceptions. However, I centralize my error handling by using dedicated scripts based on error type - rather than dealing with them inline at the time they occur. I'm handing them off to an error handler script.
Since there are classes of errors, I think we should define those. Here are the errors I've come across.
As you can see per my CF named Error() this one function pre-sets an assumed error type of FileMaker. This can be overridden with a variable of the same name which is defined after a call to Error().
The Error() CF in turn uses ErrorData(), which is where all data relevant to when the error happened is captured. It also uses the ErrorString() to get the string of the error.
My goal for error handling was to centralize. So here is an example of my handling.
You can see that I broke things down into error "classes" or types (as stated above). The script above calls the Plugin Handle Error wherein I have an error handler script for each type of error. This isolates the types of errors out, making them easy to distinguish from each other.
Note the call to Error() first which pre-sets and then is overridden by setting a subsequent $errorType.
I took this approach because the most basic solution may only have FileMaker errors to worry about and using a system of overrides provides more flexibility.
And here is an example of my standard error handler script.
To top this all off. I would suggest we define specifics such as a table named Errorlog. My Errorlog table simply captures all error information by creating a new record. Each field within the Errorlog uses an auto-enter calculation which references any of the possible $errorVariables. It does this only if $errorCapture is True.
This makes it very straight forward to capture and deal with errors.
I certainly think my own system can be refined and improved.
Let's start to define some specifics.
Matt Petrowsky
For the sake of simplicity. I would suggest we take the approach of managing an errorPackage (which we define in the spec) which includes all error related information. You can see in my script above that I have over 10 $errorSomething variables and $errorData is packed with all kinds of extra stuff that is useful for debugging/troubleshooting - and can be logged.
It's also important to note that it's not a good idea to simply start using any range of numbers for solution errors. For example. Just because you start your own errors at 5000 and above does not preclude FileMaker from using that range themselves. Unlike 192.168.0.0, FileMaker has not declared a "safe" range for errors.
This is why I propose we use an $errorType which allows developers to use any numerical range desired.
Jeremy Bante
An Error pseudo-struct? I like it. Since $errorData is packed with debugging info, why not suggest that this error structure simply include the same data as the Debug function (whatever that may be for a particular solution's purposes), in addition to the error specific information.
My first instinct is to go on to suggest an error interface for several custom functions along the lines of UUIDGetTimestamp and UUIDGetNICAddress (ErrorCode ( $error ), ErrorType ( $error ), ErrorHostTimestamp ( $error ), etc.), but I'm more ambivalent on this one. That would be a lot of parameters to write functions for, but that's probably just the same resistance others are feeling against the Layout[LayoutName] functions idea. On the other hand, continuing to structure errors the way you've got them now follows the same format as the implementation we've got for name-value script parameters, and it would be so easy to just write #Get ( $error ; "errorCode" ) knowing that. (It would be even easier if the contents of $errorData were appended to the rest of the information rather than having a nested structure.) A compromise might be ErrorGet ( "errorCode" ), so that at least the interface is independent of the implementation.
I like an $errorType parameter, but I think that should be included as another parameter to the ErrorString function. (While I'm thinking about it, the name "ErrorDescription" seems a bit more ... descriptive of what that function returns.) Then non-FileMaker errors have a centralized interpretation for any given solution, and the $errorString parameter can be excised from the error structure.
Jeremy Bante
It looks like your error handling example is following the arrow anti-pattern. (Oh the horror!) The second error handling example on the proposal avoids that with guard clauses.
I think we have two different ideas of what we mean when we talk about throwing an exception. It seems to me that you mean it in the sense of how a script deals with errors internally: drop the error in some other script's lap. I mean it in the sense of how a script behaves with respect to another script that called it: if I can't handle the error, pass the buck up the chain of command to the calling script. Both approaches have their merits. Perhaps these should be separate Accepted Techniques pages?
Matt Petrowsky
Ouch! The accusations! That arrow hurts. (not really - because I know I'm only two levels in for the default handling. And it's only when I get to level 3 that I start to evaluate if I should refactor.
As stated, it's an evolutionary mishmash and needs to be cleaned up.
I think it's admirable to take the "highbrow" approach and to educate on error vs. fault vs. exception vs. whatever, but for most of the developer's following this site I would assume they know "something went wrong". So the question is "What do I do when that happens?"
I say we address both sides, returning exceptions and handling the faults. That's what most people are looking for - even if they don't know the difference.
Defining this outside a CS context is what will make it the most approachable.
Jeremy Bante
I agree completely that making this as approachable as possible is worthwhile. There is plenty of revision to be done. One challenge we run into with that is that there's more than one good answer to "What do I do when that happens?", and there's some 'splaining to do to describe the differences and relative merits of different approaches. I'm thinking of the C.S. terms as a shorthand to be expanded in successive edits.
I also think those in the FileMaker community with a more intellectual approach will appreciate that this content is more broadly informed. We are not an ignorant island re-creating the wheel. Those without a C.S. background who are curious about such things may get some value from a FileMaker-specific introduction to more abstract topics.
Jeremy Bante
I like the idea of expanding more on Error logging. Perhaps:
It will also be worth discussing different patterns for when to log errors. For example:
Perren Smith
Wow...where have I been? All this great discussion on error handling and not a peep from myself. Oops...reality bites sometimes. :)
Anyway, based on the work already done I combed through my day job solution to see if any patterns emerged on how others treat errors in a multi-developer environment. Here's a list of observations to discuss (or not):
Enough on that for now...just adding some food for thought from a system that's seen all types and kinds of users and developers.
PS: Thanks for the reminder on respecting error capture state. That's just good stuff to write down somewhere!
Matt Petrowsky
Perren, in attempt to make things as simple for myself as possible, I use a centralized Error Handling script.
This can then branch based on the error types. I've classified errors as being one of the four possible categories.
By using a system of reserved $errorVariables and overrides, I think my system is pretty straight forward. It takes more of the Try-Catch approach by simply making a call to a single script named Handle Error ( error ) right after any step which may generate an error. It uses the Error functions of
I've put all this into the Standards.fp7 file as a start for refinement. It'd be great if you could walk through it and see what you think.
Areas which need to be addressed are multiple plugin error handling and how Custom Functions might leverage this same system.
Here's the link to get the file. Just right-click on Standards.fp7 and choose Save As...
https://github.com/filemakerstandards/fmpstandards
Jeremy Bante
I think error handling would be more digestible if we break it up into a couple different pieces:
Any others?
Matt Petrowsky
I think that's a great idea. Just add sub pages under this main page of Error Handling. I think most devs conceptually wrap the whole thing under the term "Error Handling", although the handling part is only one aspect - as you mention.
We might be able to get away with just two categories, since most of what I have with the Error function stuff is error tracking - which leads into error logging.
How about we start with
Here's how I see my system fitting into Guard Clauses, where the Handle Error script simply centralizes and manages the dialog display and logging aspects. It would also act as a dispatch for the different error types if you wanted to branch for various plugin handling (which is what I've done in the past)