What are some good patterns for VBA error handling?

What are some good patterns for error handling in VBA?

In particular, what should I do in this situation:

... some code ...
... some code where an error might occur ...
... some code ...
... some other code where a different error might occur ...
... some other code ...
... some code that must always be run (like a finally block) ...

I want to handle both errors, and resume execution after the code where the error may occur. Also, the finally code at the end must always run - no matter what exceptions are thrown earlier. How can I achieve this outcome?


Solution 1:

Error Handling in VBA

  • On Error Goto ErrorHandlerLabel
  • Resume (Next | ErrorHandlerLabel)
  • On Error Goto 0 (disables current error handler)
  • Err object

The Err object's properties are normally reset to a zero or a zero-length string in the error handling routine, but it can also be done explicitly with Err.Clear.

Errors in the error handling routine are terminating.

The range 513-65535 is available for user errors. For custom class errors, you add vbObjectError to the error number. See the Microsoft documentation about Err.Raise and the list of error numbers.

For not implemented interface members in a derived class, you should use the constant E_NOTIMPL = &H80004001.


Option Explicit

Sub HandleError()
  Dim a As Integer
  On Error GoTo errMyErrorHandler
    a = 7 / 0
  On Error GoTo 0
  
  Debug.Print "This line won't be executed."
  
DoCleanUp:
  a = 0
Exit Sub
errMyErrorHandler:
  MsgBox Err.Description, _
    vbExclamation + vbOKCancel, _
    "Error: " & CStr(Err.Number)
Resume DoCleanUp
End Sub

Sub RaiseAndHandleError()
  On Error GoTo errMyErrorHandler
    ' The range 513-65535 is available for user errors.
    ' For class errors, you add vbObjectError to the error number.
    Err.Raise vbObjectError + 513, "Module1::Test()", "My custom error."
  On Error GoTo 0
  
  Debug.Print "This line will be executed."

Exit Sub
errMyErrorHandler:
  MsgBox Err.Description, _
    vbExclamation + vbOKCancel, _
    "Error: " & CStr(Err.Number)
  Err.Clear
Resume Next
End Sub

Sub FailInErrorHandler()
  Dim a As Integer
  On Error GoTo errMyErrorHandler
    a = 7 / 0
  On Error GoTo 0
  
  Debug.Print "This line won't be executed."
  
DoCleanUp:
  a = 0
Exit Sub
errMyErrorHandler:
  a = 7 / 0 ' <== Terminating error!
  MsgBox Err.Description, _
    vbExclamation + vbOKCancel, _
    "Error: " & CStr(Err.Number)
Resume DoCleanUp
End Sub

Sub DontDoThis()
  
  ' Any error will go unnoticed!
  On Error Resume Next
  ' Some complex code that fails here.
End Sub

Sub DoThisIfYouMust()
  
  On Error Resume Next
  ' Some code that can fail but you don't care.
  On Error GoTo 0
  
  ' More code here
End Sub

Solution 2:

I would also add:

  • The global Err object is the closest you have to an exception object
  • You can effectively "throw an exception" with Err.Raise

And just for fun:

  • On Error Resume Next is the devil incarnate and to be avoided, as it silently hides errors