There are many operations in FileMaker that can result in an error. Error handling is meant to let developers enforce the desired behavior once an error occurs.
Errors typically surface to the developer via 2 main vectors allowing for detection:
Get ( LastError )
to determine the error code for the step. With that code, it is possible to know if the step was successful or if it raised an error.?
as the result, or an error string that begins with ?
like with the JSON functions. Get ( LastError )
is not impacted by those errors.Get ( LastError )
constitutes the crucial part of an error, there is a lot of information aside from the error code itself that can prove beneficial, or essential even, in understanding why the error occurred. When debugging, more information can lead the developer to a faster resolution, avoiding what caused the error in future script executions. In production, coupled with error logging, having more information will make it easier for the developer to explain why the error took place and what, if anything, should be done about it.Get ( LastError )
function, developers have to sometimes test for Get ( LastError )
and test their calculation results at other times.It is our opinion that the issues above can discourage developers from handling errors altogether, meaning that developers affected by those issues will eventually make a determination between the effort of appropriately handling errors of a given script step versus the perceived likeliness of an error happening, a tradeoff of sorts where even if a developer knows an error can happen, it is too inconvenient to handle it.
This best practice will aim at reducing the effort of error detection & error handling by addressing the issues mentioned above.
To augment the information available to the developer when errors occur, we suggest using a set of custom functions . The custom functions exhibit a similar output to Get ( LastError )
as they give back an error code. This specific trait means the functions can be used as a test by flow control structures just like the Get ( LastError )
function. The additional information gets packaged in a reserved variable $error
itself instantiated by the custom function as a JSON object.
ErrorFmp ()
and ErrorApp ()
both behave in the same fashion, aside from the fact that ErrorApp ()
lets you specify your own error code as specific circumstances in your code may require. Those functions are meant to be used as part of flow control steps ( If
, ExitLoopIf
).
The ErrorInScriptResult ()
function is meant to be called after a step that normally generates a script result ( Perform Script
, Perform Script On Server
, etc.). It performs some tests to determine if an error occurred as the step was performed or if the subscript itself returned an error object. Similar to the above functions, this function is also designed to be called as part of flow control steps. Note that this function also instantiates the reserved variable $result
.
Once the script flow has been routed to the dedicated portion of script meant to do something specific with the error, the developer can call on ErrorFound ( $error )
to determine if an error occurred. The developer can also use the native JSON functions to retrieve specific informations from the error object.
The $error
payload can be customized by specifying the $errorLevel
desired in the running script. Additional information can also be included via the details
parameter of the functions.
This best practice relies on the single-pass loop pattern for flow control. The single-pass loop is a loop where the script step just before End Loop
is Exit Loop If [True]
. This guarantees the loop will never iterate on itself. Within the loop, we can then use the Exit Loop If
step to test for errors ( error detection ) and exit outside of the loop at the bottom of the script where errors are then handled. This gives code that is DRYer than its equivalent when not relying on the single-pass loop, and when combined with the custom functions mentioned above, considerably reduces the effort of handling errors.
This best practice introduces the following nuances about implementing the single-pass loop pattern.
Get ( LastError )
output ahead of the single-pass loop, in order to avoid altering the value given by that function before the script execution (as can be seen with script triggers).Exit Loop If
where the developer calls on the ErrorFmp ()
function for error detection.Set Variable [ $userAbortIsAllow ; Value: Get ( AllowAbortState ) ]
Set Variable [ $errorCaptureIsDisabled ; Value: Get ( ErrorCaptureState ) = 0 ]
Allow User Abort [ Off ]
Set Error Capture [ On ]
Set Variable [ $error ]
The custom functions can also be used to relieve a developer from having to test the output of a calculation for a specific string like ?
.
For instance, assuming the custom function isSquare
define like this:
Case (
Sqrt ( number ) = Int ( Sqrt ( number ) ) ;
True;
// Else
False & Left ( Error ( "calculation" ; -2 ; "Not a square." ; "\"isSquare\" custom function raised this error." ) ; 0 )
)
With the function, the developer can then write a portion of script like this:
Set Variable [ $myNumber ; Value: 7 ]
Set Variable [ $myNumberIsSquare ; Value: isSquare ( $myNumber ) ]
Exit Loop If [ ErrorFound ( $error ) ]
Or, in this case, because the function itself outputs a boolean result, we can take this one step further like this:
Set Variable [ $myNumber ; Value: 7 ]
Exit Loop If [ not isSquare ( $myNumber ) ]
In both cases, as we exit the loop, the $error
variable will contain an error object.
This best practice requires the use of reserved variables. Some may find this practice objectable, but in this case it is introduced to make the general pattern less verbose and disruptive for the developer reading the script. That choice is by design, but we acknowledge that error handling can be achieved without it should that be your preference.
The $error variable is one of only a few variables that these standards considers a reserved variable