VBA- Handling Errors in all procedures with a UDF - vba

I am dealing with a good amount of workbooks with many procedures. The deeper I go in my project, the more I try to write functions or sub to process common tasks.
First of all, as the final user will be an average Excel user, I decided to protect and centralize all my code in a personal macro workbook. It sounds to me a good idea, but maybe some of you, Excel gurus, don't agree.
Then I wrote a Public Sub ErrorHandler () to deal with errors in all my procédures. I am fully aware of the Error Bubble Up. My procedure will be soemthing like this:
' All déclarations are outside
Public Sub ErrorHandler ()
Lg1 = Err.Number ' Lg1 is Long
Select Case Lg1
Case 1004
MsgBox ("File is not fund. Please verify its path.")
Exit Sub '!EDITED! <GoTo NextCode:> is much better indeed
' ...more code with other errors...
End Select
NextCode: ' !EDITED!
' ...some cleaning and initialize variables to nothing or 0
Lg1 = 0
Err.Clear
End Sub
Then I would like to use this sub in other sub this way :
On Error Go To MyError: ' <On Error call ErrorHandler> is not permited
'...more code....
MyError:
call ErrorHandler
Now a few questions:
Is it a good practice ? If not, what would you recommend ?
In case 1004 for example, when I say Exit sub, will the ErrorHandler sub itself go to end and do the cleaning stuff, or will it stop too ?
Any good hints about common and useful user defined errors (513-65535) ?
Thank you for help.

When you do "Exit Sub" in the ErrorHandler(), it will of course leave the ErrorHandler() immidiately and return to the caller, so no cleanup is done. You can of course remove the "Exit Sub", so your cleanup will run, but it will always be the same for all cases.
The Problem I see in this approach comes after returning from ErrorHandler(). You return to the caller with absolutely no idea what ErrorHandler() did. How are you going to continue from there? Catching and Displaying the Error is only half the solution. You need to come up with a way to continue from there. If it's a "common Task" you are in, you need to cancel/abort/resume that in some way or at least return some error code to it, so it knows to end gracefully.
VBA itself is a bit bad at this situation. We use http://www.everythingaccess.com/vbwatchdog.htm in our projects to handle this problem as it provides a powerful generic error handling method, quite as you aim to do.

Related

What role do modules play in writing code that is less memory-intensive?

At work, I'm venturing into the world of VBA to try to make a spreadsheet template, which will run a report when a command button is clicked.
On several occasions I have encountered an "Out of Memory" run-time error, which has been easily fixed by jigging with the code using combinations of suggestions by other users on this site!
However, I am now curious about how to make codes less memory-intensive. In particular, do modules help make code less memory-intensive and, if so, how should I use them effectively in this regard? For example, should I assign a Sub to each module or would that be overkill?
I'm new to VBA so any/all help and criticism is very welcome!
Short answer: None whatsoever.
An "out of memory" error has many reasons to occur, none of which relate to how many modules your code is organized in. Because that's all modules are: an organizational tool at your disposal.
It is completely impossible to diagnose an "out of memory" error without knowing anything about what the code is doing: <meta> you would have to narrow down the problem to a particular specific piece of code before you can ask about it on this site (dumping a whole module and asking "what's wrong with this code?" isn't going to fly) </meta>
Look for very, very large arrays, clipboard operations perhaps. Maybe you're running a loop that's attempting to create and store a bajillion New large objects - could be pretty much anything... just not the number of modules.
should I assign a Sub to each module or would that be overkill?
As with many things, the answer is "it depends". Once you've familiarized yourself with the language, and start learning about Object-Oriented Programming (OOP), you'll come across design guidelines and principles that strongly advocate towards keeping public interfaces as simple as possible: adhering to the Interface Segregation Principle (the "I" of "SOLID") means that this is a perfectly complete and acceptable class module to have:
'#Interface IComparable
'#ModuleDescription("Defines a generalized type-specific comparison method that a class implements to order or sort its instances.")
Option Explicit
'#Description("Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes (-1), follows (1), or occurs in the same position in the sort order (0) as the other object.")
Public Function CompareTo(ByVal other As Object) As Integer
End Function
But that's well beyond beginner-level procedural programming concepts.
[...] which will run a report when a command button is clicked.
That command button could be an ActiveX button, or a Shape object that's attached to a macro. Assuming it's ActiveX, the worksheet's code-behind might look like this:
Option Explicit
Private Sub RunSalesReportButton_Click()
SalesReportMacro.CreateWeeklyReport
End Sub
And then you can have a SalesReportMacro standard module that might start out like this:
'#ModuleDescription("A macro that generates the weekly sales report.")
Option Explicit
Option Private Module
'#Descrition("Generates the weekly sales report.")
Public Sub CreateWeeklyReport()
If Not RefreshSalesData Then Exit Sub
CreatePivotReport
End Sub
The Public procedure at the top has a very high abstraction level: it's easy to tell at a glance what the procedure is going to be doing, because the lower-level procedures have meaningful names that tell us exactly what's going on. These lower-level procedures would be right underneath:
Private Function RefreshSalesData() As Boolean
Dim reportWeek As String
reportWeek = PromptForReportWeek
If Not IsNumeric(reportWeek) Then Exit Function
On Error GoTo CleanFail
AdjustDataConnectionCommand reportWeek
RefreshSalesData = True
CleanExit:
Exit Function
CleanFail:
MsgBox "Could not refresh the data for week " & reportWeek & ".", vbExclamation
Resume CleanExit
End Function
And the deeper you go, the lower the abstraction level becomes:
Private Function PromptForReportWeek() As String
Dim currentWeek As Integer
currentWeek = GetCurrentWeekNumber
PromptForReportWeek = InputBox("Please specify week#", "Generate Report", currentWeek)
End Function
Private Function GetCurrentWeekNumber()
GetCurrentWeekNumber = 42 'todo
End Function
Private Sub AdjustDataConnectionCommand(ByVal weekNumber As String)
With ThisWorkbook.Connections(1).OLEDBConnection
.CommandText = "dbo.WeeklySalesReport " & weekNumber
.Refresh
End With
End Sub
Private Sub CreatePivotReport()
'todo
End Sub
Procedures (Sub, Function, and others you'll discover in due time) should have one purpose, and be kept as short and focused on that one task as possible. By doing that, you make it easier to later refactor your code - say in 2 months you need to add another ActiveX button for a new CreateWeeklyInventoryReport macro: there's a good chance that this new report will also need to PromptForReportWeek - if that functionality is already well-abstracted into its own scope, you can more easily just remove/cut it from that module, add a new CalendarParameterPrompt module (which... might end up only having that procedure for a while.. until you need a good place to put a similar PromptForReportMonth function), add/paste it in there, make it Public, and invoke it from any other macro/module.
Doing this instead of massive omnipotent procedures that know everything and do everything (and eventually stop compiling because they got so large VBA refuses to compile them), will spare you lots of headaches in the near future.

Pattern to handle expected errors locally, rethrow unexpected errors

Sometimes a certain bit of code will raise an error in an expected way, and it's most convenient to handle it locally rather than throw it to an error handling routine where it will get mixed up with other errors of the same type. Yet you don't want unexpected errors to be swallowed; you want them to be raised as usual.
In the (slightly contrived) example below, the FindInArray function can raise different types of error. One of them, ERR__ELEMENT_NOT_FOUND_IN_ARRAY, is more or less expected and so I want to handle it locally. But other error numbers may also occur, and if so I want them to be dealt with by the error handling routine.
I find that if I deal with some expected error numbers locally, I can't easily "rethrow" unexpected error numbers to be dealt with elsewhere.
How do I segregate the expected errors I want to deal with locally, from unexpected errors to be dealt with in error handling routine (or elsewhere)?
On Error GoTo ErrorHandler
'Some code...
'Here I want to trap a likely/expected error locally, because the same
'error may occur elsewhere in the procedure but require different handling.
On Error Resume Next
personIndex = FindInArray(personName, personArray)
If Err.Number = ERR__ELEMENT_NOT_FOUND_IN_ARRAY Then
MsgBox "Name not found in person array. Using default person."
Else
'What if it's a different kind of error?
' .e.g. ERR__ARRAY_CONTAINS_TWO_PERSONS_WITH_SAME_NAME
'I want to rethrow it, but can't because On Error Resume Next swallows it.
End If
On Error GoTo ErrorHandler 'back to normal
'I can't rethrow it here either, because On Error Goto cleared the Err object.
'-----------------------
ErrorHandler:
Select Case Err.Number
Case ERR__ELEMENT_NOT_FOUND_IN_ARRAY
'The error number doesn't give me enough info
'to know what to do with it here!
Case ERR__ARRAY_CONTAINS_TWO_PERSONS_WITH_SAME_NAME
'Existing code to deal with this error
Case ...
I guess I could "save" the error Number, Source, Description, etc. in some other variable / object, and use those to raise an error after On Error GoTo ErrorHandler 'back to normal, (and in fact I have implemented this just to see) but that seems terribly inconvenient and clumsy.
I made a user-defined type that has the same members as the Err object (Number, Source, Description, etc.). The SaveErr function will basically copy the values of the Err object properties into a variable of this type, and RaiseSavedErr will raise an error using those property values.
Of course the exact same thing could be done using a class and methods instead of a user-defined type and functions/subs. But the idea would be the same.
Example:
On Error Resume Next
personIndex = FindInArray(personName, personArray)
savedErr = SaveErr(Err) 'Save values of Number, Source, Description, etc.
On Error GoTo ErrorHandler
'Segregate error handling strategies here using savedErr
If savedErr.Number = ERR__ELEMENT_NOT_FOUND_IN_ARRAY Then
MsgBox "Name not found in person array. Using default person."
Else
RaiseSavedErr savedErr 'rethrows the error
End If
I'd like to know if there is a more standard or elegant way of doing this.
This answer is my opinion on the problem at hand, perhaps viewed from a slightly different angle.
When considering this block of code:
On Error Resume Next
personIndex = FindInArray(personName, personArray)
If Err.Number = ERR__ELEMENT_NOT_FOUND_IN_ARRAY Then
MsgBox "Name not found in person array. Using default person."
Else
End If
You mention: "expected errors" in the title.
But the thing is that no error should be thrown if you know in advance that it may occur.
They are a form of validation that should in my opinion be built in into the functions in the form of conditional statements.
The before mentioned code block would be something like this on a basic level:
If Not (in_array(vArray, "Jean-Francois")) Then
MsgBox "Name not found in person array. Using default person."
End If
Which in my opinion is a lot cleaner and readable.
With a custom function that is not part of the base code, but that does your check behind the scenes. Reusable functions can be wrapped in a module that you use in a way that is very similar to a static class.
Public Function in_array(vArray As Variant, sItem As String) As Boolean
Dim lCnt As Long
in_array = False
Do Until lCnt = UBound(vArray) + 1
If StrComp(vArray(lCnt), sItem, CompareMethod.Text) = 0 Then
in_array = True
Exit Function
End If
lCnt = lCnt + 1
Loop
End Function
Even better would be to use the in_array() function from within the findInArray() function and have only 1 line of code in the basesub, which would be:
personIndex = FindInArray(personName, personArray)
Let the functions in the back handle the rest and intercept exceptions that you can foresee.
This is only an example, obviously you write the functions and return values that are useful for you and you could probably add more extensive validation.
My point is that these return values are return messages that are a part of the application / validation logic, I don't see them as technical errors - hence, I don't see any benefit in using an error handler for them as a custom created function exactly fits your needs in a (my opinion) much cleaner structure.
I consider it a technical error when you pass for example three arguments into the function call while it only accepts two. The error handler notifies you, after which the developer may decide to make the current function more dynamic by allowing eg. optional parameters and fixing the bug.
Though I am a bit confused by the question asked (and I've read it over quite a lot of times by now :-)), I have a very strong feeling that the source of this dilemma lies within function scopes.
If it's ok, I will use some basic examples that show a pattern but are not 1-1 on par with your code.
How do I segregate the expected errors I want to deal with locally,
from unexpected errors to be dealt with in error handling routine (or
elsewhere)?
I kind of feel that the answer lies within the question itself.
Error handlers are functional within the local scope of sub routines / function that you call from a lower level sub routine or function.
I find that if I deal with some expected error numbers locally, I
can't easily "rethrow" unexpected error numbers to be dealt with
elsewhere.
You can if you delegate the code that you want to check for local errors to external functions / sub routines that you place on top of a certain level in the call stack. Since they handle errors within their own scope, they won't mix up with each other.
Consider this code:
Sub baseSub()
Dim n As Integer
n = checkDivision(1, 0)
n = 1 / 0 ' cause an error
End Sub
Public Function checkDivision(iNumerator As Integer, iDenominator As Integer)
On Error Resume Next
checkDivision = iNumerator / iDenominator
If Err.Number <> 0 Then
checkDivision = Err.Number
Exit Function
End If
End Function
On the contrary: when applying On Error Resume Next from a baseSub, all functions that are placed on top of the call stack will ignore the errors as well. But, it doesn't work the other way around.
I think you may use this to your advantage.
So to conclude, I believe that you can solve the problem by trapping
the expected errors in the delegated functions that you place on higher levels
of the call stack.
If this doesn't work, then I'm out of ideas.
On Error Resume Next is the root of all evil in VBA ;)
I have not seen your entire code but what you have asked in the question can easily be catered by using MULTIPLE ERROR HANDLERS & RESUME. It is much simpler than creating your custom Err object and raising error events...
Public Sub sixsixsixBytes()
On Error GoTo COMMON_ERROR_HANDLER
'Some code...
On Error GoTo ARRAY_ERROR_HANDLER
Call Err.Raise(123) 'lets say error occured in personIndex = ....
'it will jump to 2nd error handler and come back
'some code again...
'If statement is not required at all
Call Err.Raise(666)
On Error GoTo COMMON_ERROR_HANDLER:
'some code again...
Call Err.Raise(234)
Exit Sub
'# MULTIPLE ERROR HANDLERS
COMMON_ERROR_HANDLER:
Select Case Err.Number
Case 234: MsgBox 234
Case 345: MsgBox 345
End Select
ARRAY_ERROR_HANDLER:
Select Case Err.Number
Case 123:
MsgBox "Name not found in person array. Using default person."
Resume Next 'or Resume after changing a value (as per your need)
Case 666:
MsgBox "Some other error"
Resume Next
End Select
End Sub

How to call another module without returning to the first one after completion?

This is probably the dumbest question I've ever asked here, but it's hard to find answers to things like this.
I have a program with a bunch of modules/subs that each calculate a different variable. They're pretty complex, so I like to keep them separate. Now I want an earlier module to skip to another module based on user input. I thought I could use the call (sub name) method for this, but then the program returns to where the call line was and continues on that module from where it left off.
Example:
Module 1:
Sub NewPracticeSub()
Call otherpracticesub
MsgBox ("We've gone back to this sub... :(")
End Sub
Module 2:
Sub otherpracticesub()
MsgBox ("We're in the other practice sub!")
End Sub
I don't want it to return to Module 1. What can I do to have it switch control to Module 2 without it then returning to complete Module 1 upon completion of Module 2?
I feel like I just used the most confusing language possible to explain all of this, but thank you for your help anyways!!
Edit: I know I used the words module and sub interchangeably, and I know they're different. I like to keep each sub (which are each very large in my program) in their own modules because it's easier to keep track of them, and easier to explain/demonstrate the application flow to other people.
I think all you're looking for is the command Exit Sub which will make the program leave the subroutine without continuing any further, But the way you usually want to do this is, rather than calling a Sub, rather call a Function that returns a boolean value.
So, for example:
Public Function MyFunc() as Boolean
....
If [good] MyFunc = True
Else MyFunc = False
End Function
Then you could do something along the lines of:
Sub MyCallingSub()
...
If MyFunc = True then Exit Sub
Else ...
End Sub
It just adds in A LOT more felxibility and ability to choose whether you want to continue further in your sub or not.
Hope that makes sense.
Other than using the ugly End statement which I will describe below (and strongly recommend you to avoid), I'm not aware of any way to circumvent the call stack. Even John's response necessarily returns to the calling procedure, and evaluates another statement to determine whether to proceed or end.
This may yield undesirable outcomes, which is why I hesitate to recommend it, in favor of properly structuring your code, loops, etc., with respect to the call stack.
In any case, here is how you can use the End statement within your child subroutines, without needing any sort of public/global variables. This still allows you the flexibility to decide when & where to invoke the End statement, so it need not always be invoked.
Sub NewPracticeSub()
Call otherpracticesub, True
MsgBox ("We've gone back to this sub... :(")
End Sub
Sub otherpracticesub(Optional endAll as Boolean=False)
MsgBox ("We're in the other practice sub!")
If endAll then End '## Only invoke End when True is passed to this subroutine
End Sub
Why I say this method should be avoided, via MSDN:
"Note The End statement stops code execution abruptly, without
invoking the Unload, QueryUnload, or Terminate event, or any other
Visual Basic code. Code you have placed in the Unload, QueryUnload,
and Terminate events of forms and class modules is not executed.
Objects created from class modules are destroyed, files opened using
the Open statement are closed, and memory used by your program is
freed. Object references held by other programs are invalidated.
The End statement provides a way to force your program to halt. For
normal termination of a Visual Basic program, you should unload all
forms. Your program closes as soon as there are no other programs
holding references to objects created from your public class modules
and no code executing."
It will always return but that doesn't mean its a problem. I suggest you use Exit Sub as follows:
Sub NewPracticeSub()
Call otherpracticesub
**Exit Sub**
'Nothing more can execute here so its no longer a worry
End Sub
Module 2:
Sub otherpracticesub()
MsgBox ("We're in the other practice sub!")
End Sub

Do we need to create a error handler for each subroutine?

I copy a piece of code from SO as an example. The subroutine contains an error handler. Should one make an error handler for all Subs?
Public Sub SubA()
On Error Goto ProcError
Connection.Open
Open File for Writing
SomePreciousResource.GrabIt
ProcExit:
Connection.Close
Connection = Nothing
Close File
SomePreciousResource.Release
Exit Sub
ProcError:
MsgBox Err.Description
Resume ProcExit
End Sub
And by the way, how does the flow of the control inside a subroutine when the code executor encounter a Exit Sub, End Sub and Resume? And when it encounters a label such as ProcError: during the execution, does it execute it, or does it skip it?
The short answer is: No, not only do you not need to have an error handler in each procedure, but in fact you would usually not want an error handler in each procedure.
You will want to do the error handling where it makes most sense to do it. Often, you would only want an error handler in the highest-level procedure, i.e. the one that calls all the others; lower-level procedures should kick the problem upstairs and let errors "bubble up" to the higher-level procedure. Sometimes you will want some error handling in lower-level procedures.
For more, I refer you to these two excellent answers by #jtolle:
VBA Error "Bubble Up"
Handling errors in math functions
Also, an internet search will reveal that there is a whole literature on the web about error handling. Some of it is quite wrong, in my opinion! But if it sticks to what I wrote in the first two paragraphs, then it's worth considering.
Exit Sub and End Sub are fairly intuitive: the former stops execution of the current Sub and returns control to the procedure that called it (or stops execution entirely if the procedure was not called by another procedure). The latter is just a indication to the compiler that this where the code for this particular Sub ends -- and if executed, End Sub behaves like Exit Sub.
Resume specifies what should happen next, after an error-handling routine is finished. Plain Resume returns to the same statement that caused the error and tries to execute it again. Resume Next skips the statement that caused the error, and instead goes to the statement immediately following it. Resume mylabel goes to label mylabel:.
If a label such as your ProcError: is encoutered in the course of execution, then nothing special happens, and execution moves on to the next statement after the label. Of course in your example, ProcError: will never get executed directly (i.e. not unless an error is raised) because there's an Exit Sub just before it.
By the way, the ProcExit: block should probably start with an On Error Resume Next (i.e. keep on closing everything and exiting regardless of any errors) or alternatively, as pointed out by #Phydaux, an On Error Goto 0 (on error, stop execution), otherwise if something in there triggers an error, you may get into an infinite ping-pong loop between the error handler and the ProcExit: code.
ProcExit:
On Error Resume Next ' or, alternatively, On Error Goto 0
Connection.Close
Connection = Nothing
Close File
SomePreciousResource.Release
Exit Sub
Exit Sub will exit the subroutine immediatly like return in Java
End Sub is just the marker for the end of the sub routine block like } in Java
A label is simply a mark in the code wich is used to define a jump destination. In case you did not jump to the label but arrived there "regularly" the label itself will be ignored but the code after the label will be executed as if there was no label, the code in your example will be executed all the way to the Exit Sub statement as long as no error occurs. If one occures it will jump to ProcError
Resume will in this case execute ProcExit see more here

VBA Error "Bubble Up"

I haven't read much about it, but the author at the link below recommends that I don't use "bubble up" to centralize error handling in VBA.
Excel Programming Weekend Crash Course via Google Books
But I'm not sure why he recommends that, and he doesn't really explain.
Can someone tell me why I should put error handling in EVERY procedure instead of using "bubble up"? Or at least, do you know why the author says not to?
Thanks.
The short answer to your first question is "you shouldn't put an error handler in EVERY procedure". But usually some routines do need them!
To say that "every procedure must have an error handler" is in general terrible advice. The flaws with VBA error handling have been much discussed elsewhere. Conceptually, though, it's not all that different from the more standard form of exception handling found in other languages. Most of the best practices from those languages apply. You should handle errors at the lowest level where handling them makes sense. Sometimes this is in the procedure where the error occurred, many times not.
Often the most meaningful thing an internal routine can do when an error occurs is just let it pass on up the stack so it can reach code that knows what to do with it. It really depends on the routine and how it fits with the rest of the program.
Consider these examples:
A handler in a calling procedure will handle all errors raised by the routines it calls. So if a particular routine doesn't need any cleanup, then don't put any error-handler code there:
Sub Caller()
On Error GoTo HANDLER
ChildProc
On Error GoTo 0
Exit Sub
HANDLER:
Debug.Print Error, "Parent cleanup - something happened in either this procedure or a procedure that it called"
End Sub
Sub ChildProc()
Debug.Print 10 / 0 ' causes error
'Don't bother handling errors here since there's nothing this routine can do about them
End Sub
On the other hand, you may need cleanup tasks, in which case you need an error handler.
Sub Caller()
On Error GoTo HANDLER
ChildProc
On Error GoTo 0
Exit Sub
HANDLER:
Debug.Print Error, "Parent cleanup"
End Sub
Sub ChildProc()
'Pretend this routine gets ahold of some resource that must be cleaned up when it's done
call get_resources()
On Error GoTo HANDLER
Debug.Print 10 / 0 ' causes error
On Error GoTo 0
'Clean up once we're done
call release_resources()
Exit Sub
HANDLER:
Debug.Print Error, "Child cleanup"
'Clean up in case of an error
call release_resources()
'Raise another error if necessary to let callers know something went wrong
Err.Raise 10000, "ChildProc", Error
End Sub
The above examples are just meant to illustrate the point about why you might need or not need an error handler in a given routine. So it's worth noting that in real code the "resource" example is usually handled better with an RAII technique where the error handling is encapsulated with the resource acquisition and release - see https://stackoverflow.com/a/3792280/58845 for a VBA example. And things like whether to re-raise a caught error are also situation-dependent. Sometimes it's possible to handle the error entirely locally and then there is no need to tell callers that anything went wrong.
The answer to your second question is that the author doesn't seem to understand exception handling very well. He admits that error handling is context specific, but then seems to suggest that every procedure should locally decide between "correct the problem right here and resume execution" and "terminate the program". He leaves out the usually correct option, which is "clean up locally and kick the problem upstairs". So routines with no need to clean up locally should just let errors "bubble up".
My 2 cents:
You should put error handlers on all Public Procedures and Events. This means that the procedure at the bottom of the call stack will always have an error handler. Then add error handlers in your other procedures as it makes sense. If an error occurs in a procedure that does not have an error handler, it will "bubble up" to the top level error handler where it be logged/displayed in a professional fashion.
A scenario where you might want to add an error handler to a private (lower level) procedure is this:
The code needs to be fast. You have a rare condition that can be avoided, but will force you to perform an expensive logical test inside of a loop (or worse a nested loop).
You might perform the logical test in the error handler, and if it's said "rare occurrence" make the correction and resume. As the condition is rare, you will see performance gains for most conditions. If the error handler can't figure out and correct the problem then re-raise the error to bubble it on up the stack.
Obviously this is just one scenario.
I'm not sure what the default error handling of VBA is, but since its Visual Basic for Applications, and those applications include things like excel and word, I assume just a dialog box will appear which will not be helpful to the user.
I assume that the author has been bitten by code not handling errors so he now recommends all procedures to handle errors.
The full answer is that you have to be aware of every error that can occur and to have code in place to handle it, whether it is as low as possible (where you may not know what to do), or as high as possible (which means less effort writing error handling code, but not knowing why the error occurred), or strategically (which is just in the right places where you should be able to recover from most common errors) or just everywhere (which may be just too much development effort).
I see at least one reason in his explanation: because doing so deprives you from the benefit of Resume (next).
Plus you won't know in which module the error happened.
It is better not to use "bubble-up" part of error handling because errors should be handled & if it is known as to what to do, if such an error occurs - is better known to procedure as to what to do than the calling procedure.
Sub test()
On Error GoTo e
Dim c As Integer
Dim d As Integer
c = add(5, 0)
d = divideWhichManagedItsOwnErrorHandling(5, 0)
d = divide(5, 0)
Exit Sub
e:
MsgBox "error occurred somewhere for which I don't know what to do: " + Err.Description
End Sub
Function add(a As Integer, b As Integer) As Integer
add = a + b
End Function
Function divide(a As Integer, b As Integer) As Integer
divide = a / b 'if error occurs, it will "bubble-up" to the caller.
End Function
Function divideWhichManagedItsOwnErrorHandling(a As Integer, b As Integer) As Integer
On Error Resume Next
Dim result As Integer
result = a / b
If Err.Number = 11 Then 'if divide by zero occurred, user must have passed 0 for b
result = 0 ' return 0 if the divide by zero occurs.
End If
divideWhichManagedItsOwnErrorHandling = result
End Function