General error handling - but correctly

Josef P.

Well-known member
Local time
Today, 16:01
Joined
Feb 2, 2023
Messages
991
Since I see it again and again that unrecoverable errors in procedures are displayed to the user with a MsgBox but then the code continues as if nothing had happened, I put the problem up for discussion in this thread.

For example, this error handling is incorrect:
Code:
Private Sub MainProc()

   Dim x As Long
   Dim y As Long
 
On Error GoTo HandleErr
 
   x = 1
   y = Func1(x)
 
   MsgBox "Result: " & y
 
ExitHere:
   Exit Sub
 
HandleErr:
   MsgBox "Error: (" & Err.Source & ") " & Err.Description
   Resume ExitHere
 
End Sub

Public Function Func1(ByVal x As Long) As Long

On Error GoTo HandleErr

   Func1 = x * Func2(x)
 
ExitHere:
   Exit Function
 
HandleErr:
   MsgBox "Error: (" & Err.Source & ") " & Err.Description ' <--- WRONG!
   Resume ExitHere

End Function

Public Function Func2(ByVal x As Long) As Long
   Func2 = x / 0 ' to raise an error
End Function
In this example, no error handling in Func1 would have been better.

How to do it right?
The MsgBox could be replaced by an extra ErrorHandler procedure, but this must allow the error to be passed up to the top level.

Is there interest to create such a (global) error handling together?
However, I would not come immediately with a code proposal, but first work out a concept together.
Note: All who use vbWatchdog do not need this. ;)

Attached is an example file to show the problem. (Hopefully I managed to make it reasonably understandable in the code ;)).
Of course, the example creates the error artificially. Therefore, please take this as a given and think only about error handling and not about error prevention.
 

Attachments

I'm going to dispute (SLIGHTLY) that your view is universally correct. I am NOT saying you are totally wrong - just that you have glossed over a factor that I think would be a valid part of this discussion.

In the contrived case you presented, you are correct that there is almost no point for the FUNC1 error routine. But your contrivance hides the fact that the error routine COULD have done more in the called routine, such is placing a default value (for the after-error case) if there was such a possibility. I fully agree that not every routine needs an error handler, but there are times when you need the error handler - not to provide a usable value after the error, but to more precisely identify the faulting routine. In your contrived case if you leave out FUNC1's error handler, you don't know whether the error actually occurred in FUNC2, FUNC1, or MAINPROC. IF that identification is important, you need the error handler - not to FIX the running process but to FIND where to look for the error so you can fix the design of the process.

Which means that error handling requirements can change over time.
 
I exclude expected errors in a general error handling, because one will handle them deliberately in the respective procedure.
If an unexpected error occurs in a function, I consider it dangerous if the function returns a value.
I would possibly implement this for known/expected errors, if it fits the function. In most cases I would rather leave the error, because there is more information than in a "dummy value".
But of course, it depends on the particular function and its intended use.

In your contrived case if you leave out FUNC1's error handler, you don't know whether the error actually occurred in FUNC2, FUNC1, or MAINPROC.
Exactly. But you can design the error handling in such a way that the Err.Source is passed up and does not simply "clear" the error.
A possible (unfinished) variant is in the attachment of #1.

related to Func1:
Code:
Public Function Func1(ByVal x As Long) As Long

On Error GoTo HandleErr

   Func1 = x * Func2(x)

ExitHere:
   'GoSub CleanUp ' <-- if required (in this function no clean up is required
   Exit Function
 
CleanUp:
   ' clean up code
   ' ...
   Return
 
HandleErr:
   ' here could run a cleaned up code (use only if required)
   ' GoSub CleanUp
   ' then raise error again
   Err.Raise Err.Number, "Func1->" & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
                         ' ^- call stack to Err.Source
                                
End Function
BUT: so this function can no longer be used as a top-level function.
 
Last edited:
When creating the sample file, I noticed something:

If I use the function Calculate0 (has no error handling) directly in ControlSource, as expected an unhandled runtime error comes (in the accdb), and I get to the error location with debug.
Control Source: =Calculate0([txtParam1];[txtParam2])
EH_2a_Calc0.png
EH_2a_Calc0_debug.png

If I click on "End", "#Error" is displayed in the control.
EH_2a_Calc0_ErrEnd.png

So far, everything is as expected.

Next, I added a private method to the form class to catch and report the error.
Code:
Private Function CalculateProxy(ByVal P1 As Variant, ByVal P2 As Variant) As Variant

On Error GoTo ErrHandler

   CalculateProxy = Calculate0(P1, P2)

ExitHere:
   Exit Function

ErrHandler:
   MsgBox "Calculation failed" & vbNewLine & "Error: (CalculateProxy->" & Err.Source & ") " & Err.Description
   Resume ExitHere

End Function
Control Source: =CalculateProxy([txtParam1];[txtParam2])
=> If there is an error, the self-generated error message appears. ... also as expected.
EH_2a_CalcProxyEH.png


But now comes something unexpected for me:
I removed error handling from the form function CalculateProxy, with the expectation that the same behavior would occur as when using =Calculate0([txtParam1];[txtParam2]).
Code:
Private Function CalculateProxy(ByVal P1 As Variant, ByVal P2 As Variant) As Variant
   CalculateProxy = Calculate0(P1, P2)
End Function
But this was not the case.
EH_2a_CalcProxyNoEH.png

=> No error message came at all and the control just stayed empty.


Since I use vbWatchdog in my applications, I have not noticed this behavior before.
Has it always been like this?

/edit: new test file attached
 

Attachments

Last edited:

Users who are viewing this thread

Back
Top Bottom