Why VBA goes to error handling code when there is no error? - vba

I have writen some code in VBA (Excel) with error handling labels. It worked fine until I recently notice the error handling code gets executed everytime, not just when an error occurs. Does anybody know why this happens? Thanks.
Here's a trivial test case where both msgboxes would pop up.
Sub example()
On Error GoTo err_handle
MsgBox "OK!"
err_handle:
MsgBox "not OK"
End Sub

You want to add an Exit Sub to your routine:
Sub example()
On Error GoTo err_handle
MsgBox "OK!"
Exit Sub
err_handle:
MsgBox "not OK"
End Sub
Look here for a full explaination.

It's because you aren't returning out of the subroutine after the first message box the (OK) one. After that is shown the next line of code is executed which is the "not ok" message.
You could either exit the subroutine early before your error handler label (ExitSub) or goto the end of the subroutine on success (bypassing the "error" routine)

need to add exit sub else the program continues to execute the label as it is the part of the code

Just as a clarification to add some meat to the other answers.
"err_handle:" is only error handling code because you are using it as such. It isn't intrinsically an error handler like a catch block in other languages.
Technically "err_handle:" is just a label that facilitates a goto jump. In your case the goto just happens to be used with an error handler (on error goto)

The generally accepted pattern for error handling is to have an error handler and an exit procedure. A pretty standard code stump might look like this:
Public Function Example() As String
Dim strRtnVal As String 'Return value variable.
On Error GoTo Err_Hnd
'***************************************************************************
'Lock interface code here (hourglass, screenupdating etc.)
'***************************************************************************
'***************************************************************************
'Your code here.
'***************************************************************************
Exit_Proc:
'Prevents "Error Loops" caused by errors within the exit procedure:
On Error Resume Next
'***************************************************************************
'Restore Interface.
'***************************************************************************
Example = strRtnVal 'Set Return Value.
Exit Function
Err_Hnd:
'Display message, do some logging, whatever.
Resume Exit_Proc '<- Run exit procedure.
End Function

Related

Type mismatch on err

I'm using GoTo for error handling in an Access module and am getting a type mismatch error on the Procedures.HandleError call.
I tested to see if err is an Error:
Exit Sub
catch:
If IsError(err) Then
MsgBox "yes"
Else
MsgBox "no"
End If
Procedures.HandleError "ctrCreateSubject, frm_OnCreate", err, True
End Sub
and the MsgBox displays no and I can't figure out why. I'm using the same syntax in other places without problems
Can anyone help?
Let me expand a bit on my comment with an example.
The On Error GoTo statement will take care of the IsError() part since the procedure will jump to the catch label, only if there's an error. Therefore, if we do jump into the error handler, then we definitely have an error.
A sample error handler:
Sub Whatever()
On Error GoTo catch
'do something
Leave:
Exit Sub
catch:
'If we hit this point, then we definitely have an error.
'At this point, we can query the error number if we want to take action based on the error.
If Err.Number = xxxx Then
Msgbox "Error " & xxxx
End If
Resume Leave
End Sub
Then, there's another approach if you want to suspend the error handler and then query if an error occurred.
On Error Resume Next
'do something
If Err.Number <> 0
'An error occurred
End If
Which we can then clear if we want to do this again later on on our method.
Err.Clear
Lastly, keep in mind Err is a global object so you don't need to create an instance. Further info on MSDN: Err object

Why does error handler trigger with no error? [duplicate]

This question already has answers here:
Why VBA goes to error handling code when there is no error?
(5 answers)
Closed last year.
I am running one line of code to open a PDF when a button is clicked. I put an error handler on there in case the file doesn't exist. The problem is that the code works and the file is opened but the error handler still runs.
When I disable the error handling code it runs fine and VB doesn't generate an error. I even tried using Err.Description for the message box and it just pops up with nothing displayed. Here is my code:
Private Sub cmdOpenPDF_Click()
On Error GoTo Errorhandler
Application.FollowHyperlink "E:\groups\folder\folder2\" & Dir("E:\groups\folder\folder2\*" & Me.txtInvNum & "*")
Errorhandler:
MsgBox "Unable to locate the file. I'm sorry this has happened.", vbOKOnly
Exit Sub
End Sub
Since the PDF opens with no problem I would expect there to be no error. It's also really puzzling that there is no error when run without the error handler. Does anyone know why it's doing this?
In your code, Errorhandler is merely a line label, with the On Error statement redirecting evaluation to this label in the event of an error.
As such, consider that VBA does not distinguish between the line label used for the purpose of an error handler, or any other line label used in conjunction with any other GoTo statement, or a line label which is not referenced at all.
In the absence of an error, your code is equivalent to:
Private Sub cmdOpenPDF_Click()
Application.FollowHyperlink "E:\groups\folder\folder2\" & Dir("E:\groups\folder\folder2\*" & Me.txtInvNum & "*")
Errorhandler:
MsgBox "Unable to locate the file. I'm sorry this has happened.", vbOKOnly
Exit Sub
End Sub
Here, VBA will dutifully execute the FollowHyperlink method, followed by the MsgBox function, and then finally the Exit Sub statement.
To avoid the error handler being evaluated every time, a typical implementation of an error handler will include an additional label signifying a clean exit prior to the error handler label, e.g.:
Private Sub cmdOpenPDF_Click()
On Error GoTo Errorhandler
Application.FollowHyperlink "E:\groups\folder\folder2\" & Dir("E:\groups\folder\folder2\*" & Me.txtInvNum & "*")
ExitSub:
Exit Sub
Errorhandler:
MsgBox "Unable to locate the file. I'm sorry this has happened.", vbOKOnly
Resume ExitSub
End Sub
This means that in normal circumstances, the Exit Sub statement will be reached before evaluation of the code following the Errorhandler label.

How to resume error handling from the calling function while in a subfunction in VBA?

When I write in VBA for Word or Excel, I typically have an error handler in my main function and call several subs from it, and most of the time, I want subs' messages to get caught in the main function. Typically everything works great with this strategy, and it mimics what I'm used to in C++.
However, I run into trouble when I need a different type of error handling in one or two subs.
For example, when I need to turn on Resume Next for the sake of checking if an object fails and is set to nothing. When I want to turn error handling on, my MainErrorHandler is now out of scope.
Sub Main()
On Error GoTo MainErrorHandler
Application.ScreenUpdating = False
Call OpenFile
Call SubWithOwnErrorHandling
'Do more stuff
GoTo CleanExit
MainErrorHandler:
MsgBox Err.Description
CleanExit:
Application.ScreenUpdating = True
End Sub
Sub OpenFile()
On Error Resume Next
Set objFile = objFSO.OpenTextFile(fileLocation & fileName, 1)
On Error GoTo ErrorHandler ' Label Not Defined!
If objFile Is Nothing Then
Call Err.Raise(2009, , "Out File doesn’t exist.")
End If
End Sub
Likewise, when I want to have a sub handle errors locally and occasionally elevate an error, I'm not sure how exactly to do that.
Sub SubWithOwnErrorHandling()
On Error GoTo SubErrorHandler
isReallyBad = True
If isReallyBad Then
Call Err.Raise(2020, , "Error that needs to cause application to exit!")
Else
Call Err.Raise(2001, , "Error that just needs the function to exit!")
End If
SubErrorHandler:
On Error GoTo MainErrorHandler ' Label Not Defined!
If Err.Number = 2020 Then
Call Err.Raise(2020, , Err.Description)
End If
End Sub
Is there any way to do what I'm trying to accomplish for either case?
Labels are always local.
On Error is always local too - heck, its deprecated ancestor was On Local Error!
So you can't GoTo-jump between procedure scopes (THANK GOD!!)
This means at any given time, there's only ever one of two things the run-time can do On Error:
Jump to a local error handler
Blow up the current stack frame and see if the caller handles it
[ignore the error and happily keep running blindfolded under blue skies and sunshine]
That third point, you guessed it, is what On Error Resume Next does.
One critical error you've done, is specifying an On Error statement inside an error-handling subroutine, and the error-handling subroutine runs regardless of whether you're in an error state or not. That makes following execution extremely confusing, even if that label was legal. Exit Sub or Exit Function (or heck, Exit Property, depending on what's your scope) before the handler, and make sure error-handling code is only ever hit in an error state.
Resetting error handling
So, one thing you want to do, is to reset error handling - here:
On Error Resume Next
Set objFile = objFSO.OpenTextFile(fileLocation & fileName, 1)
On Error GoTo ErrorHandler ' Label Not Defined!
You know objFSO.OpenTextFile can possibly blow up, and you want to handle it yourself, i.e. deal with the objFile Is Nothing possibiilty manually. You can absolutely do that, but then what you need is this:
On Error Resume Next
Set objFile = objFSO.OpenTextFile(fileLocation & fileName, 1)
On Error GoTo 0
On Error GoTo 0 resets error handling, i.e. the next instruction to throw an error will bubble up the call stack, until everything goes up in flames.
Custom Errors
The next thing you want to do, is to raise custom errors.
If isReallyBad Then
Call Err.Raise(2020, , "Error that needs to cause application to exit!")
Else
Call Err.Raise(2001, , "Error that just needs the function to exit!")
End If
That's pretty easy actually - but it's easier with an Enum:
Public Enum AppCustomError
ERR_ReallyBad = vbObjectError + 42
ERR_ReallyReallyBad
ERR_VeryReallyTerriblyBad
ERR_YouGetTheIdea
End Enum
The vbObjectError constant ensures that your custom error numbering doesn't step on toes; your error numbers will all be negative - and with an Enum for each possible error you can throw, you don't need to care what the actual error number is, so you let the enum member mechanics do their thing (e.g. ERR_ReallyReallyBad will be ERR_ReallyBad + 1, automatically).
Then you can do this (assuming you're in a class module - otherwise replace TypeName(Me) with some string literal, or skip it):
On Error GoTo ErrHandler
If isReallyBad Then
Err.Raise ERR_VeryReallyTerriblyBad, TypeName(Me), "Blow up the app!"
Else
Err.Raise ERR_ReallyBad, TypeName(Me), "Blow up this function!"
End If
Exit Sub
ErrHandler:
With Err
Select Case .Number
Case ERR_VeryReallyTerriblyBad
.Raise .Number 'rethrow
Case ERR_ReallyBad
'function blew up, we're done here.
'...
End Select
End With
And then the calling code, which has its own error-handling subroutine, can decide that it can't deal with ERR_VeryReallyTerriblyBad, and just blow everything up by rethrowing:
Exit Sub
MainErrorHandler:
With Err
Select Case .Number
Case ERR_VeryReallyTerriblyBad
.Raise .Number 'rethrow
Case Else
MsgBox .Description
End Select
End With

Excel VBA error handling not working for second error

In the below code Errorhandler correctly takes care of first error when I enter a workbook that is not open or any random string. But when I click on retry and again enter a random name I get "Subscript out of Range" error # Workbooks(workbookname). Activate.
Can anyone help me why it is happening and how can I make it work. I have tried a lot of things. But nothing is working. This code is part of a larger program.
Sub test()
Dim workbkname As String
On Error GoTo Errorhandler
Retry:
workbookname = InputBox("Enter workbook name:", _
"Name Enrty")
If StrPtr(workbookname) = 0 Then
MsgBox ("Aborting Program")
End
End If
Workbooks(workbookname).Activate
Exit Sub
Errorhandler:
Response = MsgBox("Workbook " & workbookname & " not found", vbRetryCancel)
If Response = 4 Then
GoTo Retry
End If
End Sub
The issue here is that the VBA Error Handler does not clear the error once given a directive like GoTo. As a result, the code thinks that it has encountered an error within your error handling routine and thus throws the error up the stack.
In order to clear the error, you must either call Resume with a place to resume (either Resume alone to run the erroneous code again, Resume Next to resume the line of code following the error, or Resume can be called followed by a label, as below:
Sub ErrTest
On Error Goto ErrHndl
For i = 0 to 5
a = 1/i
nextiLabel:
Next i
Exit Sub
ErrHndl:
Resume nextiLabel
End Sub
You can also use Err.Clear() to remove the error, but that is less recommended.
See here for more info: Error Handling in Excel

Error handling only works once

I have a very simple VBA code that should try to open a nonexistent file, send me to an error handler, then back to my code in an infinite loop (intentionally).
However, the compiler only catches the error the first time, then breaks on the second pass.
I have tried every combination of On Error statements to send it back on the second pass, but nothing seems to work.
Here is the code:
Sub TestError()
On Error GoTo errError
lblError:
On Error GoTo errError
'Code that should raise an error and send to errError
Excel.Application.Workbooks.Open ("lakdfjldkj")
Exit Sub
errError:
MsgBox "Code didn't break"
GoTo lblError
End Sub
Is there something I'm missing, or is this a bug?
Don't test this unless you are at peace with having to kill Excel, or add an extra condition for looping
Use Resume to... well... resume processing after the error handler.
Sub TestError()
On Error GoTo errError
'Code that should raise an error and send to errError
Excel.Application.Workbooks.Open "lakdfjldkj"
Exit Sub
errError:
MsgBox "Code didn't break"
Resume
End Sub
I have used code like this to access a certain worksheet, if it is not found then the error handler creates one with the correct name and then resumes the processing.
You need to clear the error message by using:
Err.Clear
Source: https://msdn.microsoft.com/en-us/library/hh2zczch(v=vs.90).aspx
Basically it still thinks you're handling the error, and hasn't reset the handling mechanism, so it doesn't re-trigger until you clear it.
(Resume also clears the Error as well, as you can note by the reference above).
I have tested, it is possible to type :
Resume label1 (line label)
Resume next
Resume 110 (line number)