In VBA error handling is done by on error statement.
I want to temporarily change the error handling and then go back to the previous behavior afterward. How would it be possible to check the current error handling and store it in a variable (I couldn't find anything in the references)?
'set the error handling to s.th. "on error... "
'some code with the regular error handling
'change the error handling to "on error ..." (regardless of what it was before)
'some code with the new error handling
'change back to the previous error handling
'some code with the regular error handling
Background: I needed to do a is nothing check on a Variant array to exclude empty object indexes from being used, but is nothing applied to an array index that holds a value throws an exception, so I temporary wanted to change the error handling to on error resume next. Eventually is solved this using a different approach but I'm still wondering if I can determine the current error handling somehow during runtime Here's the question and answer to my original problem.
EDIT: I know I can check my previous code manually to find out what type of error handling has been used. However I want to avoid that (to save time).
I suppose as a workaround I could set an additional variable with the state which I can then check for the current state, although this will result in quite a bit of overhead. Something like this:
Dim errorHandling as String
errorHandling = "resumeNext"
on error resume next
'some code
'changing the error handling temp.
'some other code
'changing the error handling to it's previous state
if errorhandling = "resumeNext" then
On Error Resume Next
elseif errorhandling = "GoToErrorhandler" then
On Error GoTo errorhandler
End If
'Rest of the code
Read/Write to Array
Option Explicit
Sub ReadWriteArrayExample()
Dim myArray() As Variant: ReDim myArray(1 To 10)
Dim i As Long
Dim n As Long
' Fill the array.
For i = 1 To 10
n = Application.RandBetween(0, 1)
If n = 1 Then ' write a random number between 1 and 10 inclusive
myArray(i) = Application.RandBetween(1, 10)
'Else ' "n = 0"; leave the element as-is i.e. 'Empty';do nothing
End If
Next i
' Debug.Print the result.
Debug.Print "Position", "Value"
For i = 1 To 10
If Not IsEmpty(myArray(i)) Then ' write the index and the value
Debug.Print i, myArray(i)
'Else ' is empty; do nothing
End If
Next i
End Sub
Error Handling
Sub ErrorHandling()
Const ProcName As String = "ErrorHandling"
On Error GoTo ClearError ' enable error trapping
' Some code
On Error Resume Next ' defer error trapping
' Some tricky code
On Error GoTo ClearError ' re-enable error trapping
' Some Code
ProcExit:
Exit Sub
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Sub
Related
I can't find where the error is. A similar code to pull dynamic file name worked in another tab.
Sub MonthlyBCRCPL()
Dim filePath As String
Dim CardsRCPLWb As Workbook
Set CardsRCPLWb = ActiveWorkbook
filePath = CardsRCPLWb.Sheets("BCRCPL").Range("A1").Value
'Optimize Code
Call OptimizeCode_Begin
Const FlashFolder As String = "\\apacdfs\SG\GCGR\GROUPS\ASEAN\Dashboard\Cards\Flash\"
Flashname = Format(CardsRCPLWb.Sheets("ASEAN - CARDS, RCPL").Range("C2").Value, "YYYYMMDD")
Flashname = "ASEAN SD Regional Dashboard - " & Flashname & ".xlsx"
Flashpath = FlashFolder & Flashname
Dim FlashWb As Workbook
Set FlashWb = Workbooks.Open(Flashpath)
If FlashWb Is Nothing Then MsgBox "SD Flash File does not exist": Exit Sub
Consider handling the error in the subroutine and have it raise a message. Then, properly continues/skips/exits rest of code even releasing objects from memory caught during the exception. This is a best practice in VBA (and generally in programming).
I suspect the path cannot be found which looks to be a network UNC, file naming is not valid such as use of special characters, or workbook does not exist when trying to open:
Sub MonthlyBCRCPL()
On Error Goto ErrHandle:
...code...
ExitSubBlock:
Set CardsRCPLWb = Nothing
Set FlashWb = Nothing
Exit Sub
ErrHandle:
Msgbox Err.Number & " - " & Err.Description, vbCritical, "RUNTIME ERROR"
Goto ExitSubBlock
' Resume Next
End Sub
I have the following problem
I have a VLOOKUP that I want to run in a loop, but when vlookup doesnt find a corresponding value then it halts the script. If I handle the error with error handler, it will jump and also halt it anyway.
Sub Botón1_Haga_clic_en()
Dim filas As Integer
Dim desdefila As Integer
filas = InputBox("Cuantas files tiene éste bloque de pagos?")
desdefila = InputBox("Desde que fila empieza?")
filasfinal = filas + desdefila
For x = desdefila To filasfinal
Dim Nombre As String
Dim Rango As Range
Set Rango = Sheets(6).Range("A:B")
Nombre = Application.WorksheetFunction.VLookup(Range("A" & desdefila).Value, Rango, 2, 0)
Range("E" & desdefila).Value = Nombre
desdefila = desdefila + 1
Next
End Sub
Any ideas on how to go back to the loop or handling this error ?
You can handle a Vlookup error using the following structure:
Dim myValue As Variant
myValue = Application.VLookup("Some Value", Range("A:B"), 2, False)
If IsError(myValue) Then
'Code for when not found
Else
'Code for when found
End If
Note that this does not use Application.WorksheetFunction.VLookup, but instead uses Application.VLookup.
So, for your code, the error handling would be inserted to look something like:
Dim Nombre As Variant
Dim Rango As Range
Set Rango = Sheets(6).Range("A:B")
Nombre = Application.VLookup(Range("A" & desdefila).Value, Rango, 2, 0)
If IsError(Nombre) Then
'Code for when not found
Else
Range("E" & desdefila).Value = Nombre
End If
desdefila = desdefila + 1
The early-bound VLookup function from WorksheetFunction will raise a runtime error if the lookup fails.
If a failing lookup is an exceptional thing and you want to handle it cleanly, you need an error-handling subroutine:
On Error GoTo ErrHandler
For ...
Nombre = Application.WorksheetFunction.VLookup(Range("A" & desdefila).Value, Rango, 2, 0)
'...
Next
Exit Sub
ErrHandler:
' execution jumps here in case of a failed lookup
MsgBox "Lookup of '" & Range("A" & desdefila) & "' failed. Please verify data."
'if you want to resume the loop, you can do this:
Resume Next
'otherwise execution of the procedure ends here.
End Sub
If you know a failing lookup is a possibility but not quite exceptional, and you just want to deal with it and move on, you can use On Error Resume Next / On Error GoTo 0 to hide the error instead:
On Error Resume Next
Nombre = Application.WorksheetFunction.VLookup(Range("A" & desdefila).Value, Rango, 2, 0)
If Err.Number <> 0 Then Nombre = "Not Available"
Err.Clear
On Error GoTo 0
Alternatively, you can use the late-bound version (as in elmer007's answer) which extends the Application interface; instead of raising a VBA runtime error when lookup fails, it returns an error value that you can check with the IsError function:
Dim Nombre As Variant 'note "As Variant"
For ...
Nombre = Application.VLookup(Range("A" & desdefila).Value, Rango, 2, 0)
If IsError(Nombre) Then
'handle error value
End If
'...
Next
One of the advantages of using the early-bound version, is that you get IntelliSense for the parameters, whereas you need to know exactly what you're doing when you use the late-bound version.
I have the following code:
ErrNr = 0
For Rw = StRw To LsRw 'ToDo speed up with fromrow torow
If Len(ThisWorkbook.Sheets(TsSh).Cells(Rw, TsCl)) = 0 Then
ThisWorkbook.Sheets(TsSh).Cells(Rw, TsCl).Interior.ColorIndex = 46
ErrNr = ErrNr + 1
End If
Next
My problem is if there is an error on the page, my code is not running after that. I think the solution should be with:
On Error Resume Next
N = 1 / 0 ' cause an error
If Err.Number <> 0 Then
N = 1
End If
But I don't know how to use this code.
It depends on what you want to do.
On Error Resume Next will ignore the fact that the error occurred. This is a great way to get your code to execute to completion, but will just about guarantee that it won't do what you want.
On Error Goto 0 is the default response. It will pop up the error message that VBA is generating
On Error Goto <label> will cause your code to jump to a specified label when an error occurs and allows you to take an appropriate action based on the error code.
The last option, On Error Goto <label> is usually the most useful, and you'll want to do some digging on how to best use it for your application.
This site is where I got the details above from, and is usually the first results that comes from Googling for "excel vba on error". I've used that reference myself a number of times.
I have interpreted your requirement as to how to handle common worksheet errors when looping through a range of cells and examining the values. If you attempt to look at a cell that contains an error (e.g. #N/A, #DIV/0!, #VALUE!, etc) you will get something like:
Runtime error '13':
Type mismatch.
These can be caught with VBA's IsError function.
Dim rw As Long
With ThisWorkbook.Sheets(TsSh)
For rw = StRw To LsRw
If IsError(.Cells(rw, 1)) Then
.Cells(rw, 1).Interior.ColorIndex = 10
ElseIf Not CBool(Len(.Cells(rw, 1).Value2)) Then
.Cells(rw, 1).Interior.ColorIndex = 46
End If
Next rw
End With
In the above, I am catching the cells with an error and coloring them green. Note that I am examining them for errors before I check for a zero length.
I generally try to avoid On Error Resume Next as it will try to continue no matter what the error (there are some cases where it's useful though).
The code below passes all errors out of the procedure where they can be handled on a case by case basis.
Sub test1()
Dim n As Double
On Error GoTo ERROR_HANDLER
n = 1 / 0 ' cause an error
On Error GoTo 0
Exit Sub
ERROR_HANDLER:
Select Case Err.Number
Case 11 'Division by zero
n = 1
Err.Clear
Resume Next
Case 13 'Type mismatch
Case Else
'Unhandled errors.
MsgBox "Error " & Err.Number & vbCr & _
" (" & Err.Description & ") in procedure test1."
Err.Clear
End Select
End Sub
This question already has answers here:
Why VBA goes to error handling code when there is no error?
(5 answers)
Closed last year.
I have my code below, the strange thing is that the Errorhandler procedure is still executing even though there are no errors in the code... What can be the issue?
Running the code without any errorhandlers generates no errors, but still the msgbox under Errorhandler shows up when I include an error handling statement!
Code
Public Sub ExportGraphs(Optional PivotExport As Boolean)
' Exports only graphs on the "Mainwindow" sheet to a new worksheet
Dim wsh As Worksheet: Set wsh = Sheets.Add
Dim source_sht As Worksheet: Set source_sht = Sheets("Mainwindow")
ActiveWindow.Zoom = 70
On Error GoTo Errorhandler
With wsh
If source_sht.OLEObjects("Btn_CurrentTime").Object.Value = True Then
.Name = source_sht.OLEObjects("CombBox_Instruments").Object.Value & " " & source_sht.OLEObjects("DTPicker_FROM").Object.Value _
& "-" & source_sht.OLEObjects("DTPicker_TO").Object.Value
Else
.Name = source_sht.OLEObjects("CombBox_Instruments").Object.Value & " " & "Max_Possible_To" _
& "-" & source_sht.OLEObjects("DTPicker_TO").Object.Value
End If
End With
Dim source_chart As ChartObject
Dim target_rng As Range: Set target_rng = wsh.Range("A1")
For Each source_chart In source_sht.ChartObjects
source_chart.CopyPicture xlScreen, xlBitmap
target_rng.PasteSpecial
Set target_rng = target_rng.Offset(20, 0)
Next
If PivotExport = True Then
Debug.Print "se"
End If
Errorhandler:
MsgBox "An export sheet for this ticker and timeline already exists"
End Sub
#dee provided the correct answer.
The Errorhandler: is just a place holder. It does NOT operate like you think. You are using it like an If... Then... statement:
If Error Then
Show MsgBox
Else
Skip MsgBox
End If
As the Errorhandler is just a placeholder and NOT an If... Then..., the code after the placeholder will run regardless of error or no error. To rectify this issue, add an Exit Sub above the Errorhandler: line:
Exit Sub
Errorhandler:
MsgBox "An export sheet for this ticker and timeline already exists"
End Sub
In this piece of code, ErrorHandler: is what is known as a line label.
Errorhandler:
MsgBox "An export sheet for this ticker and timeline already exists"
End Sub
Line labels are not executable code, but rather just a marker that can tell other code where to jump to via any GoTo Statement. Armed with this knowledge, they are obviously not exclusive to error handlers.
The solution here is to use the Exit Statement to return from the Sub "early".
Exit Sub
Errorhandler:
MsgBox "An export sheet for this ticker and timeline already exists"
End Sub
Others may disagree with me, but I like to build my error handling so that the code always stops execution on Exit Sub. If the code ends its execution on End Sub, something has gone wrong.
New to vba, trying an 'on error goto' but, I keep getting errors 'index out of range'.
I just want to make a combo box that is populated by the names of worksheets which contain a querytable.
For Each oSheet In ActiveWorkbook.Sheets
On Error GoTo NextSheet:
Set qry = oSheet.ListObjects(1).QueryTable
oCmbBox.AddItem oSheet.Name
NextSheet:
Next oSheet
I'm not sure whether the problem is related to nesting the On Error GoTo inside a loop, or how to avoid using the loop.
The problem is probably that you haven't resumed from the first error. You can't throw an error from within an error handler. You should add in a resume statement, something like the following, so VBA no longer thinks you are inside the error handler:
For Each oSheet In ActiveWorkbook.Sheets
On Error GoTo NextSheet:
Set qry = oSheet.ListObjects(1).QueryTable
oCmbBox.AddItem oSheet.Name
NextSheet:
Resume NextSheet2
NextSheet2:
Next oSheet
As a general way to handle error in a loop like your sample code, I would rather use:
on error resume next
for each...
'do something that might raise an error, then
if err.number <> 0 then
...
end if
next ....
How about:
For Each oSheet In ActiveWorkbook.Sheets
If oSheet.ListObjects.Count > 0 Then
oCmbBox.AddItem oSheet.Name
End If
Next oSheet
Actualy the Gabin Smith's answer needs to be changed a bit to work, because you can't resume with without an error.
Sub MyFunc()
...
For Each oSheet In ActiveWorkbook.Sheets
On Error GoTo errHandler:
Set qry = oSheet.ListObjects(1).QueryTable
oCmbBox.AddItem oSheet.name
...
NextSheet:
Next oSheet
...
Exit Sub
errHandler:
Resume NextSheet
End Sub
There is another way of controlling error handling that works well for loops. Create a string variable called here and use the variable to determine how a single error handler handles the error.
The code template is:
On error goto errhandler
Dim here as String
here = "in loop"
For i = 1 to 20
some code
Next i
afterloop:
here = "after loop"
more code
exitproc:
exit sub
errhandler:
If here = "in loop" Then
resume afterloop
elseif here = "after loop" Then
msgbox "An error has occurred" & err.desc
resume exitproc
End if
I do not want to craft special error handlers for every loop structure in my code so I have a way of finding problem loops using my standard error handler so that I can then write a special error handler for them.
If an error occurs in a loop, I normally want to know about what caused the error rather than just skip over it. To find out about these errors, I write error messages to a log file as many people do. However writing to a log file is dangerous if an error occurs in a loop as the error can be triggered for every time the loop iterates and in my case 80 000 iterations is not uncommon. I have therefore put some code into my error logging function that detects identical errors and skips writing them to the error log.
My standard error handler that is used on every procedure looks like this. It records the error type, procedure the error occurred in and any parameters the procedure received (FileType in this case).
procerr:
Call NewErrorLog(Err.number, Err.Description, "GetOutputFileType", FileType)
Resume exitproc
My error logging function which writes to a table (I am in ms-access) is as follows. It uses static variables to retain the previous values of error data and compare them to current versions. The first error is logged, then the second identical error pushes the application into debug mode if I am the user or if in other user mode, quits the application.
Public Function NewErrorLog(ErrCode As Variant, ErrDesc As Variant, Optional Source As Variant = "", Optional ErrData As Variant = Null) As Boolean
On Error GoTo errLogError
'Records errors from application code
Dim dbs As Database
Dim rst As Recordset
Dim ErrorLogID As Long
Dim StackInfo As String
Dim MustQuit As Boolean
Dim i As Long
Static ErrCodeOld As Long
Static SourceOld As String
Static ErrDataOld As String
'Detects errors that occur in loops and records only the first two.
If Nz(ErrCode, 0) = ErrCodeOld And Nz(Source, "") = SourceOld And Nz(ErrData, "") = ErrDataOld Then
NewErrorLog = True
MsgBox "Error has occured in a loop: " & Nz(ErrCode, 0) & Space(1) & Nz(ErrDesc, "") & ": " & Nz(Source, "") & "[" & Nz(ErrData, "") & "]", vbExclamation, Appname
If Not gDeveloping Then 'Allow debugging
Stop
Exit Function
Else
ErrDesc = "[loop]" & Nz(ErrDesc, "") 'Flag this error as coming from a loop
MsgBox "Error has been logged, now Quiting", vbInformation, Appname
MustQuit = True 'will Quit after error has been logged
End If
Else
'Save current values to static variables
ErrCodeOld = Nz(ErrCode, 0)
SourceOld = Nz(Source, "")
ErrDataOld = Nz(ErrData, "")
End If
'From FMS tools pushstack/popstack - tells me the names of the calling procedures
For i = 1 To UBound(mCallStack)
If Len(mCallStack(i)) > 0 Then StackInfo = StackInfo & "\" & mCallStack(i)
Next
'Open error table
Set dbs = CurrentDb()
Set rst = dbs.OpenRecordset("tbl_ErrLog", dbOpenTable)
'Write the error to the error table
With rst
.AddNew
!ErrSource = Source
!ErrTime = Now()
!ErrCode = ErrCode
!ErrDesc = ErrDesc
!ErrData = ErrData
!StackTrace = StackInfo
.Update
.BookMark = .LastModified
ErrorLogID = !ErrLogID
End With
rst.Close: Set rst = Nothing
dbs.Close: Set dbs = Nothing
DoCmd.Hourglass False
DoCmd.Echo True
DoEvents
If MustQuit = True Then DoCmd.Quit
exitLogError:
Exit Function
errLogError:
MsgBox "An error occured whilst logging the details of another error " & vbNewLine & _
"Send details to Developer: " & Err.number & ", " & Err.Description, vbCritical, "Please e-mail this message to developer"
Resume exitLogError
End Function
Note that an error logger has to be the most bullet proofed function in your application as the application cannot gracefully handle errors in the error logger. For this reason, I use NZ() to make sure that nulls cannot sneak in. Note that I also add [loop] to the second identical error so that I know to look in the loops in the error procedure first.
What about?
If oSheet.QueryTables.Count > 0 Then
oCmbBox.AddItem oSheet.Name
End If
Or
If oSheet.ListObjects.Count > 0 Then
'// Source type 3 = xlSrcQuery
If oSheet.ListObjects(1).SourceType = 3 Then
oCmbBox.AddItem oSheet.Name
End IF
End IF