4 Replies Latest reply on Dec 8, 2015 9:11 AM by jurgmay

    Error Handling

    jurgmay

      Hi all,

       

      I'm looking to gather any thoughts or opinions that you guys may have on how best to handle errors in scripts.

       

      As an example, I have a script which polls an eCommerce server every few minutes to see if any orders have been placed. Orders are downloaded as XML data. I then parse the XML and create the orders and associated line items in FileMaker and download any files which may be linked to the line items (there's a field with a URL which points to the file).

       

      The outline of my main script (using pseudo code) looks like this -

       

      Perform Script ( "Pull Pending Orders From Server" )

      Set Variable ( $xmlData ; Get ( ScriptResult )

      If ( not IsEmpty ( $xmlData ) )

      -- Perform Script ( "Create Orders" )

      -- Perform Script ( "Create Line Items" )

      -- Perform Script ( "Update Revenue Values" )

      End If

       

      Each of the three scripts 'Pull Pending Orders From Server', 'Create Orders', 'Create Line Items' could potentially create errors that I would need to handle. For example, if there is no internet connection then 'Pull Pending Orders From Server' would fail; if malformed or corrupt XML is received from the server then 'Create Orders' could fail; if any files associated with the Line Items fail to download then the 'Create Line Items' script would fail.

       

      I'm just not sure WHERE the error handling should take place.

       

      Is it best practice for the error to be raised in the relevant script and then passed back using 'Exit Script' and have each script in the Call Stack pass that error back to the main script and then handle it from there?

       

      Also, I'm thinking that it would be a good idea to create an 'Error Message' table that associates error text with an error number so that I can display a relevant message to the user based on the error that was raised. Is that a good idea?

       

      I'd appreciate any comments on how error handling in general should be undertaken.

       

      Thanks as always,

       

      Juerg

        • 1. Re: Error Handling
          Mike_Mitchell

          As a general principle, error handling should happen as close to where the error generated as practical. So yes, a best practice is to use the Exit Script [ ] step to pass the results back to the calling script.

           

          An error message table is okay. Just make sure you either have all possible error conditions in it, or have a "run home to Momma" default if it doesn't find the error code.

           

          There was a thread on the forum some time ago about parsing the FileMaker error codes page from the Help to extract the error codes and associated descriptions. Not necessarily friendly for an end user, but it might be worth looking at.

           

          HTH

           

          Mike

          • 2. Re: Error Handling
            jbante

            jurgmay wrote:

             

            The outline of my main script (using pseudo code) looks like this -

             

            Perform Script ( "Pull Pending Orders From Server" )

            Set Variable ( $xmlData ; Get ( ScriptResult )

            If ( not IsEmpty ( $xmlData ) )

            -- Perform Script ( "Create Orders" )

            -- Perform Script ( "Create Line Items" )

            -- Perform Script ( "Update Revenue Values" )

            End If

             

            Each of the three scripts 'Pull Pending Orders From Server', 'Create Orders', 'Create Line Items' could potentially create errors that I would need to handle. For example, if there is no internet connection then 'Pull Pending Orders From Server' would fail; if malformed or corrupt XML is received from the server then 'Create Orders' could fail; if any files associated with the Line Items fail to download then the 'Create Line Items' script would fail.

             

            I'm just not sure WHERE the error handling should take place.

            Error handling can't be limited to any one place in your code. Bits and pieces of error handling generally live all over the place. As Mike already mentioned, there at least has to be something as close as possible to the sources of errors. If you don't detect an error immediately after it happens, your information about what happened will get confused very fast. The result of Get ( LastError ) is only good for one non-logic script step (If, Loop, etc.), and the closer your error detection is to the source of the error, the more confidence you can have that you know exactly where the error actually happened.

             

            As for what to do about the error, again as Mike said, the more you can do right where the error happened, the better. But that doesn't get you off the hook for dealing with the error in other spots, too. If a subscript has an error, the calling script may need to do something about it, too. I like most of my scripts to return two bits of error information: $errorCode (numeric) and $errorMessage (text). I really like keeping these separate. The numeric $errorCode is easy for a script to work with:

             

            Perform Script ["Subscript"]

            #Parse ScriptResult to local variables using your method of choice

            If [$errorCode = 401]

            #Deal with this specific error

            Else If [$errorCode ≠ 0]

            #Deal with non-specific error

            End If

             

            The $errorMessage result is a human-readable (ideally user-readable, but that isn't always possible) description of (1) exactly what the went wrong and (2) what to do about it, whenever the resolution isn't obvious from (1). This is what I show in error message dialogs to users and the first thing I as a developer see before I start honing in on a fix. This error message is your opportunity to save your users (and you) time on support calls, and to give yourself a head start on diagnosing problems. I do use a custom function that returns a description for each of FileMaker's own error codes, but I don't see much value in keeping a central table or other registry (such as another custom function) linking error messages to error codes. For my error messages to describe (1) exactly what the went wrong and (2) what to do about it, each message tends to be unique to the circumstances of one spot in one script. It's easier for the error messages to be written into the script where they apply, and any central registry mechanism makes it harder for me to copy & paste a script between files, and it especially makes it harder to share my scripts with other developers who may not use the same practice.

             

            As parent scripts often need to respond to errors in sub-scripts, I think it's only polite for sub-scripts to also show some deference to what parent scripts are doing. I like to write my subscripts to respect Error Capture state the same way FileMaker's built-in script steps do, i.e. a subscript doesn't pop any error dialogs to the user if the parent script Set Error Capture [On]. The parent script is presumably operating with a bigger picture in mind than the sub-script; it may be that an abort-worthy error for a sub-script is only a negligible bump in the road for the parent script. This also means that if I turn error capture on in a script, I don't necessarily turn it off again; I always return the error capture state to what it was when the script was called, so a parent script doesn't turn it on only for a child script to carelessly and opaquely turn it off again:

             

            Set Variable [$errorCaptureWasOff; Value: Get ( ErrorCaptureState ) = 0]

            Set Error Capture [On]

            #Operation that could generate an error goes here

            Set Variable [$errorCode; Value: Get ( LastError )]

            If [$errorCaptureWasOff]

            Set Error Capture [Off]

            If [$errorCode ≠ 0]

            Show Custom Dialog[ErrorDescription ($errorCode)]

            End If

            End If

             

            For the specific situation you present with a back-to-back series of scripts, I like to do something special. With just one sub-script, the parent script error handling comes immediately after the Perform Script call:

             

            Perform Script ["Subscript"]

            #Set ScriptResult to local variables

            If [$errorCode ≠ 0]

            #Do something about errors

            End If

             

            That can get tedious fast for a back-to-back series of subscripts, so I'll pass the error information from subscript to subscript instead, which will eventually make its way back to the parent script all the same. The subscripts would all start by checking for any errors in their parameters:

             

            #Set ScriptParameter to local variables in subscript

            If [$errorCode ≠ 0]

            Exit Script [Get ( ScriptParameter )]

            End If

            #Continue with subscript

             

            Then the parent script passes the results of one script to the parameter of the next:

             

            Perform Script ["Subscript 1"]

            Perform Script ["Subscript 2"; Parameter:Get ( ScriptResult )]

            Perform Script ["Subscript 3"; Parameter:Get ( ScriptResult )]

            #Set ScriptResult to local variables

            If [$errorCode ≠ 0]

            #Do something about errors

            End If

             

            This of course only works well if the parent script doesn't intend to take any corrective action after errors in one subscript that would allow the next subscript to proceed normally.

            • 3. Re: Error Handling
              Mike_Mitchell

              Awesome writeup, Jeremy. Thanks!

              • 4. Re: Error Handling
                jurgmay

                Wow! jbante - thanks so much for such a detailed response. It's going to take me a few read throughs to let it sink in! I really appreciate you taking the time to do that. It's high time I stared getting this error handling thing nailed and that's going to help massively.

                 

                And, Mike (sorry, couldn't tag you for some reason?!) - thanks for your reply also. I appreciate the help and contributions that you and the regulars here put forward. I've asked a few questions myself recently but have learnt so much from you guys over the years by following other threads, so thanks!

                 

                Best wishes,

                 

                Juerg