Handling Recurring VBA Errors Within Subroutine - vba

I am stuck on something that I have a workaround for, but it bugs me I don't have a direct answer for how to address the issue of using On Error Goto for recurring errors. My question is essentially the same as this one, however the answers provided are alternatives to the OP's overall approach rather than how to handle the specific issue.
I've simplified an example below. I am WELL AWARE that there are probably a dozen ways this code could be rewritten to avoid any error -- I am just using this to illustrate the issue of trying to figure out why/how does this code work properly when i = 0, but not when i = 3,6,9?
Sub vbaErrorConfusion()
Dim theArray(9) As Long, i As Long
For i = 0 To 9
On Error GoTo fixingErrors
'next line will intentionally create an error when
'when i = {0} this works as expected, but at i=3 next line throws an error.
theArray(i) = i + 1 / (i Mod 3)
On Error GoTo 0
next_i:
Next i
Exit Sub
'----Error Handling----
'this works as expected when i=0 but not when i = 3,6,9
fixingErrors:
Err.Clear
On Error GoTo 0
'at this point I would expect my error state to be the same as the start of procedure?
theArray(i) = -1
GoTo next_i
End Sub
What my Err variable shows after first instance of 0.
After I run Err.Clear I would expect the behavior for i=3 to be the same as when i=0, however my procedure stops with the below VBA error one would expect WITHOUT any error catching.
I presume there's some way to reset the Error variable or handle this type of situation without a workaround? Any quality responses will get upvoted. Thanks.

To tell VBA that an you have dealt with the error you need a Resume statement. Therefore, replacing the line GoTo next_i with Resume next_i you give you the outcome you expect.
You do not need Err.Clear. Also, On Error GoTo 0 will disable the error handler. However, neither of these lines will tell VBA that you have dealt with an error.
In your code at i=0 the error handler is activated but VBA is not told that the error has been dealt with (i.e. no Resume statement). At i=3 another error occurs while a previous error hadn't been dealt with. In this case the current procedure/function/property cannot deal with the second error, which is therefore, fatal.
You should also take the On Error GoTo fixingErrors line outside the loop.
Sub vbaErrorConfusion()
On Error GoTo fixingErrors
Dim theArray(9) As Long, i As Long
For i = 0 To 9
'*On Error GoTo fixingErrors
'next line will intentionally create an error when
'when i = {0} this works as expected, but at i=3 next line throws an error.
theArray(i) = i + 1 / (i Mod 3)
'*On Error GoTo 0
next_i:
Next i
Exit Sub
'----Error Handling----
'this works as expected when i=0 but not when i = 3,6,9
fixingErrors:
'*Err.Clear
'*On Error GoTo 0
'at this point I would expect my error state to be the same as the start of procedure?
theArray(i) = -1
Resume next_i
End Sub

Please take some time to read up on how Error handling in VBA works.
Sub vbaErrorConfusion()
Dim theArray(9) As Long, i As Long
For i = 0 To 9
On Error GoTo fixingErrors
'next line will intentionally create an error when
'when i = {0} this works as expected, but at i=3 next line throws an error.
theArray(i) = i + 1 / (i Mod 3)
'On Error GoTo 0
'next_i:
Next i
Exit Sub
'----Error Handling----
'this works as expected when i=0 but not when i = 3,6,9
fixingErrors:
Err.Clear
Debug.Print "An error was generated for i= ", i
'On Error GoTo 0
'at this point I would expect my error state to be the same as the start of procedure?
theArray(i) = -1
Resume Next
End Sub

Related

Skip code if no error occurs by using an "if statement" in VBA

I need some input, since I'm quite new to VBA.
I have a part of a code that is looking for a file in three different path location. The code tries path 1, if error, it will continue to the next path (that's perfect)
My problem is, if the file path is working for code "test 2" and "test 3", It will always run until last one (i.e. "test 3") instead of jumping to next part of code. If the location path works for example for test 1 or test 2 location, I don't need to run the following lines after.
How can I make my code skip that part?
'Test 1 location path
On Error GoTo Err1:
sFldr = "Path1"
Err1:
Resume Next
'Test 2 location path
On Error GoTo Err2:
sFldr = "Path2"
Err2:
Resume Next
'Test 3 location path
On Error GoTo Err3:
sFldr = "Path3"
Err3:
Resume Next
'next part of big code
more code here
If I understand correctly, you need to progressively try values for (in this contrived example) sFldr, and the operation might error. Instead of using On Error Goto, just test the Err object for an error directly:
On Error Resume Next
sFldr = "Path1"
If Err.Number <> 0 Then
Err.Clear
sFldr = "Path2"
If Err.Number <> 0 Then
Err.Clear
sFldr = "Path3"
End If
End If
On Error GoTo 0 'Or resume your normal error handling.
'next part of big code
Call it like this:
If GetFldrValue = vbNullString Then
'whatever you need to do if all 3 fail.
End If
'next part of big code
Another option is to extract the whole thing into its own function (which might not be a bad idea - the comment "next part of big code" indicates that the routine might be doing too much). If you do that, you can just turn error handling off entirely and return the first valid value found:
Function GetFolderValue() As String
On Error Resume Next
GetFolderValue = "Path1"
If Err.Number <> 0 Then Exit Function
GetFolderValue = "Path2"
If Err.Number <> 0 Then Exit Function
GetFolderValue = "Path3"
End Function
One Procedure = One Error Handler.
Simple as that.
Make sure the error-handling subroutine only ever runs in an error state.
I can't really give you a more concrete answer than that, because the code you're showing us does literally nothing; assigning a string literal to a string variable is never going to raise an error, ...and a workflow that jumps around up and down from one line to another in the "normal" execution path isn't sane - you need to restructure things. I'd love to help, but I've no idea what your code does.
To put it shortly, you should have small procedures that look like this:
Private Sub DoSomething()
On Error GoTo CleanFail
'procedure code here
CleanExit:
'cleanup code here
Exit Sub
CleanFail:
'error-handling code here
Resume CleanExit
End Sub
Option 1: Wrap in Function
It would be best to wrap this in a function that is responsible for retrieving the value of sFldr.
Option 2: GoTo Statement - not recommended
Perhaps add a GoTo if the value of sFldr is not null
'Test 1 location path
On Error GoTo Err1:
sFldr = "Path1"
If(sFldr <> "") Then Goto ContinueFunc
Err1:
Resume Next
'Test 2 location path
On Error GoTo Err2:
sFldr = "Path2"
If(sFldr <> "") Then Goto ContinueFunc

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

Does "On Error GoTo errorHandler:" remain active for the rest of the sub if it is not triggered?

This Code tries to select a checkbox; if it does not exist it will create the checkbox via the errorHandler. I was wondering; if an error occurs later in this sub, will it still be directed to that errorHandler? In other words, will VBA attempt to create a checkbox anytime I have an error during this sub? Or does ErrorHandling only look at the next line that occurs after "On Error Goto ...:"
Sub PrepNewPurchaseEntryForm()
'no relevant VBA happens here
Dim companyCount As Integer: companyCount = colCount("CompanyUserEntries")
Call FocusOnSheet("NewPurchaseEntry")
For i = 3 To companyCount
Dim companyName As String: companyName = Sheets("CompanyUserEntries").Cells(1, i).Value
On Error GoTo Err1:
ActiveSheet.Shapes.Range(Array(companyName)).Select
''if error occurs here, will Err1 still be called?
Next i
''if error occurs here, will Err1 still be called?
Exit Sub
Err1:
Call PlaceCheckBox(("B" & (i + 7)), companyName)
Resume Next
End Sub
Sorry this might seem like a stupid question but I have been reading up on ErrorHandling but i couldn't really find anything that explicitly explained this.
On Error GoTo xxxx error handler will stay active as long as you you don't set another error handler or remove it with On Error Goto 0

Error (Run-time error 5) will not raise when "On error resume next" is active

I've provided the actual code I'm using below.
The exact condition I'm trying to handle is the strCurrentRev argument as a zero-length string. (e.g. strCurrentRev="")
If I comment out the error handling statements, trying to execute the ASC method on a zero-length string throws Run-Time Error 5 for "invalid procedure call or argument".
If I then check err.Number it's = 5.
If I try to run the exact same statement with on error resume next active, it will not raise any errors, e.g. after execution err.number is always = 0.
If on error resume next is active, and you try to execute the ASC method from the immediate window (e.g. Type asc(strcurrentrev) and hit Enter) it will throw the run-time error and set the err.number property to 5.
I've never experienced this before. Why would having on error resume next active cause the error not to raise in normal execution???
Function NextRevLetter(strCurrentRev As String) As String
'This function returns the next revision letter given an existing revision letter.
'Declare variables
Dim lngX As Long
Dim strX As String
Dim strY As String
'First, check if we are dealing with rev A-Z or AA-ZZ
If Len(strCurrentRev) <= 1 Then
'Check that we can work with revision letter ***THIS IS WHERE I AM HAVING A PROBLEM!***
On Error Resume Next
Err.Clear
'Procedure call to flag errors with ASC method without changing any values
lngX=Asc(strCurrentRev)
lngX=0
On Error GoTo 0
If Err.Number > 0 Then
Err.Clear
If Len(strCurrentRev) < 1 Then
'No revision specified, assign first revision"
strCurrentRev = "-"
Else
MsgBox "The revision letter specified is not compliant. The next revision letter cannot be determined.", vbOKOnly, "Error: Revision does not follow rules"
'Return the existing revision (no change) and exit function
NextRevLetter = strCurrentRev
Exit Function
End If
End If
'Code continues - not important for this question...
Exit Function
You're not using the right tool for the job. Runtime errors should be handled, not shoved under the carpet (because that's what On Error Resume Next does - execution happily continues as if nothing happened).
You need to try to avoid raising that error in the first place. What's causing it?
lngX=Asc(strCurrentRev)
You already know what's happening:
The exact condition I'm trying to handle is the strCurrentRev argument as a zero-length string.
Well then, the correct way to handle this is to verify the length of strCurrentRev before you pass it to the Asc function, which you know will raise a runtime error #5 if you give it an empty string!
If strCurrentRev <> vbNullString Then
'calling Asc(strCurrentRev) here will not fail!
End If
I was asked to elaborate on a better way to handle the error, and this is the easiest place to do so. I think it's okay, because in a way it does answer the original question as well. However, let me say first that the right thing to do here is to avoid the error entirely, but for the sake of completeness, there is a way to do this cleanly with an error handler.
The idea is to check the error number, handle it by fixing the value, and then resuming the next line of code.
Function NextRevLetter(strCurrentRev As String) As String
'This function returns the next revision letter given an existing revision letter.
On Error GoTo ErrHandler
'Declare variables
Dim lngX As Long
Dim strX As String
Dim strY As String
'First, check if we are dealing with rev A-Z or AA-ZZ
If Len(strCurrentRev) <= 1 Then
'Procedure call to flag errors with ASC method without changing any values
lngX = Asc(strCurrentRev)
lngX = 0
'Code continues - not important for this question...
End If
Exit Function
ErrHandler:
If Err.Number = 5 Then
lngX = 0
If Len(strCurrentRev) < 1 Then
'No revision specified, assign first revision"
strCurrentRev = "-"
Resume Next
Else
MsgBox "The revision letter specified is not compliant. The next revision letter cannot be determined.", vbOKOnly, "Error: Revision does not follow rules"
'Return the existing revision (no change) and exit function
NextRevLetter = strCurrentRev
Exit Function
End If
Else If Err.Number = someOtherExpectedError
'handle it appropriately
Else
' !!! This is important.
' If we come across an error we don't know how to handle, we re-raise it.
Err.Raise Err.Number, Err.Source, Err.Description
End If
End Function
Note that the flow of your program is not interrupted by all of this error handling and we only handle the error that we're expecting. So, if an error is raised, we recover only if we know how to. Otherwise, execution is halted.
I would still prefer just to check to see if the value is = vbNullString though.
I just figured this out. The On Error GoTo 0 statement resets the Err.Number property to 0.
Sorry for wasting anyones time!!!!

Excel VBA: On Error Goto statement not working inside For-Loop

I'm trying to cycle through a table in excel. The first three columns of this table have text headings, the rest of them have dates as headings. I want to assign those dates, sequentially, to a Date-type variable, and then perform some operations based on the date
To do this I am using a foreach loop on myTable.ListColumns. Since the first three columns do not have date headers, I have tried to set the loop up so that, if there is an error assigning the header string to the date-type variable, the loop goes straight to the next column
This seems to work for the first column. However, when the second column's header is 'assigned' to the date-type variable, the macro encounters an error even though it is within an error-handling block
Dim myCol As ListColumn
For Each myCol In myTable.ListColumns
On Error GoTo NextCol
Dim myDate As Date
myDate = CDate(myCol.Name)
On Error GoTo 0
'MORE CODE HERE
NextCol:
On Error GoTo 0
Next myCol
To reiterate, the error is thrown on the second round of the loop, at the statement
myDate = CDate(myCol.Name)
Can anyone explain why the On Error statement stops working?
With the code as shown, you're actually still considered to be within the error handling routine when you strike the next statement.
That means that subsequent error handlers are not allowed until you resume from the current one.
A better architecture would be:
Dim myCol As ListColumn
For Each myCol In myTable.ListColumns
On Error GoTo ErrCol
Dim myDate As Date
myDate = CDate(myCol.Name)
On Error GoTo 0
' MORE CODE HERE '
NextCol:
Next myCol
Exit Sub ' or something '
ErrCol:
Resume NextCol
This clearly delineates error handling from regular code and ensures that the currently executing error handler finishes before you try to set up another handler.
This site has a good description of the problem:
Error Handling Blocks And On Error Goto
An error handling block, also called an error handler, is a section of code to which execution is tranferred via a On Error Goto <label>: statement. This code should be designed either to fix the problem and resume execution in the main code block or to terminate execution of the procedure. You can't use the On Error Goto <label>: statement merely skip over lines. For example, the following code will not work properly:
On Error GoTo Err1:
Debug.Print 1 / 0
' more code
Err1:
On Error GoTo Err2:
Debug.Print 1 / 0
' more code
Err2:
When the first error is raised, execution transfers to the line following Err1:. The error hander is still active when the second error occurs, and therefore the second error is not trapped by the On Error statement.
You need to add resume of some sorts in your error handling code to indicate the error handling is over. Otherwise, the first error handler is still active and you are never "resolved."
See http://www.cpearson.com/excel/errorhandling.htm (specifically the heading "Error Handling Blocks And On Error Goto" and following section)
Follow-up to paxdiablo's accepted answer. This is possible, allowing two error traps in the same sub, one after the other :
Public Sub test()
On Error GoTo Err1:
Debug.Print 1 / 0
' more code
Err1:
On Error GoTo -1 ' clears the active error handler
On Error GoTo Err2: ' .. so we can set up another
Debug.Print 1 / 0
' more code
Err2:
MsgBox "Got here safely"
End Sub
Using On Error GoTo -1 cancels the active error handler and allows another to be set up (and err.clear doesn't do this!). Whether this is a good idea or not is left as an exercise for the reader, but it works!
Clearing all property settings of the Err object is not the same as resetting the error handler.
Try this:
Sub TestErr()
Dim i As Integer
Dim x As Double
On Error GoTo NextLoop
For i = 1 To 2
10: x = i / 0
NextLoop:
If Err <> 0 Then
Err.Clear
Debug.Print "Cleared i=" & i
End If
Next
End Sub
You'll notice the just like the OP, it will catch the error properly when i =1 but it will fail on line 10 when i = 2, even though we used Err.Clear
Dim ws As worksheets
For Each myCol In myTable.ListColumns
On Error GoTo endbit
Dim myDate As Date
myDate = CDate(myCol.Name)
On Error GoTo 0
'MORE CODE HERE
endbit:
Resume NextCol
NextCol:
' Next myCol
Exit Sub ' or something '