I have the following two lines of code:
On Error Resume Next
myWorkbook.Sheets("x").Columns("D:T").AutoFit
I've stepped into the macro and executed the line On Error Resume Next and then on the next line myWorkbook... it does the following:
Why doesn't the compiler resume the next line of code?
On Error has been liberally used throughout the procedures code; I realize best practice is to use this as little as possible but it seems to fit the purpose of this macro.
Reading this SO QUESTION it says that you can't have one set of error trapping within another. How can I guarantee that one set of error trapping has been "closed off" before the code moves on - does On Error Goto 0 reset the error trapping? If it does reset then why doesn't resume work in the following?:
Sub GetAction()
Dim WB As Workbook
Set WB = ThisWorkbook
On Error GoTo endbit:
'raise an error
Err.Raise 69
Exit Sub
endbit:
On Error GoTo 0
On Error Resume Next
WB.Sheets("x").Columns("D:T").AutoFit
End Sub
There is also a VBA setting that will cause On Error ... statements to be ignored and that dialog box to always appear. See this answer for more details on checking/changing the option: https://stackoverflow.com/a/3440789/381588
An example of an error when the initial error is not closed out.
Sub GetAction()
Dim WB As Workbook
Set WB = ThisWorkbook
On Error GoTo endbit:
'raise an error
Err.Raise 69
Exit Sub
endbit:
On Error Resume Next
WB.Sheets("x").Columns("D:T").AutoFit
End Sub
As you have found, within the same function or subroutine, On Error Resume Next doesn't override On Error Goto ... if it's still active.
You are correct that On Error Goto 0 restores the default error handler.
There are some cases where On Error is the most appropriate way to handle an exceptional condition. I prefer to use the following structure:
On Error Resume Next
statement which might fail
On Error Goto 0
if statement has failed then ...
This keeps everything together, but in other cases a generic error handler at the end of the procedure can be better.
I've found that in functions/subs that iterates over nested objects, errorhandling might be a drag in VBA. A solution that works for me to better handle complex iterations is separating setting of objects in their own functions, e.g.
main function/sub:
set FSOfolder = SetFSOFolder(FSOobject, strFolder)
Private Function SetFSOFolder(FSO as scripting.FileSystemObject, strFolder as string) as Scripting.Folder
on error resume Next
set SetFSOFolder = FSO.GetFolder(strFolder)
on error goto 0
End Function
and then in the next line in main function:
if (not fsofolder is nothing) then
I agree using On Error Resume Next is not best practice but most/many of my Access apps are not overly sensitive to minor nuances in data integrity (i.e. analytical or reporting, not transactions and not audited). For this reason I use OERN quite often because VBA is very sensitive to some situations that you cannot anticipate entirely.
1 - will the error cause data corruption. If yes handle it. Many routines I use are just crunching a large volume of records and there may be errors in imported data that haven't been fixed. Usually I have a lot of conversion processes that will let the system clean up its own data eventually.
2 - is the error frequent and non-critical (ie not a key). If yes it's OERN otherwise the error may not be predicable and you end up crashing out or have to write a bunch of I-T-E or Select Case logic to trap it.
Don't use On Error Resume Next instead write code that shouldn't crash.
Note: I am being careful how I phrase that because you never guaranty code doesn't crash. But if you use On Error Resume Next then part of the natural flow of your code is for it to crash, which is wrong, big time wrong.
Related
This question already has answers here:
Why VBA goes to error handling code when there is no error?
(5 answers)
Closed last year.
Example Code:
On Error GoTo Line3
More code
Line3:
Some code
Some code
More code
Even when there is no error, the "some code" underneath Line3 still gets run even though I don't want it to run. Otherwise when there is an error, Line3 gets run appropriately (skipping code that comes before it like it should).
Your code runs as it should. Code dealing with errors should be written outside of the main procedure, keeping in mind that your code should also try and spot potential errors and deal with them before they cause an error.
At the moment your error isn't being cleared when you jump to it, and because it's in the main body of code it's executed when your first set of More Code is finished and it reached the Line3 label.
Sub Test1()
'Ignore the error.
'MsgBox won't appear, but code won't know an error occured.
'MsgBox says all's good anyway, even though the error is still present.
Dim Rng As Range
On Error GoTo SkipLineWithError
MsgBox Rng.Address
SkipLineWithError:
MsgBox "All good, error number is " & Err.Number
End Sub
A better way is to try and catch the error before it happens:
Sub Test2()
'Checks that Rng won't throw an error if referenced.
'Code has dealt with the error and says all's good.
Dim Rng As Range
If Not Rng Is Nothing Then
MsgBox Rng.Address
Else
MsgBox "Range not set"
End If
MsgBox "All good, error number is " & Err.Number
End Sub
Sometimes, errors do occur though and they need to be dealt with properly. For this you jump out of the main procedure, deal with the error and jump back in again.
With this code notice the Exit Sub - the code between Exit Sub and End Sub is where your error handling goes. The main body of code ends when it reaches Exit Sub.
Resume tells your code where to jump back to - on its own it jumps back to the line that caused the error, Resume Next is the line after the error and Resume <label> jumps back to a label you've entered such as Resume Line3
Sub Test3()
Dim Rng As Range
On Error GoTo ErrorHandler
MsgBox Rng.Address
MsgBox "All good, error number is " & Err.Number
TidyExit:
'Close connections, general tidy up before ending procedure.
Exit Sub
ErrorHandler:
Select Case Err.Number
Case 91 'Deal with the error if it happens.
'For this we'll give Rng a default address.
Set Rng = Sheet1.Range("A1")
Resume
Case Else
MsgBox "Error couldn't be handled... display an error message."
Resume TidyExit 'Jump to end of main body of code.
End Select
End Sub
Edit: Have updated code based on comment by #VBasic2008. Was being lazy with my first code and missed a key point.
I've barely scraped the surface here, the links below should help.
on-error-statement
vba-error-handling
Using Gotos to manage execution paths is almost always a poor design choice. You could consider the following approach that does not rely so heavily on Goto labels. It is easier to see the intended logic flow (and error handling) without having to visually parse any Goto statements.
Sub Example()
If Not TryMorecode1() Then
Somecode1
Somecode2
End If
Morecode2
End Sub
Private Function TryMorecode1() As Boolean
'catch errors locally within this function
'return False if an error is generated
End Function
Private Sub Morecode2()
End Sub
Private Sub Somecode1()
End Sub
Private Sub Somecode2()
End Sub
The code will still run, it's just a label you can point the execution to if need be.
Try this to avoid your problem but without knowing your exact requirement, you’ll likely need to tweak the code structure to suit your needs.
My suggestion would be to put Line3 at the bottom, not in the middle of the routine. That way you just do a clean up and then get out. It tends to make more sense from a readability perspective. Of course, you should only do that if it makes sense to get out after the error occurs.
On Error GoTo Line3
More code
Goto Line4
Line3:
Some code
Some code
Line4:
On Error GoTo 0
More code
The trick with GoTo statements (if you intend to use them) is to use them sparingly.
I have tried using If Error GoTo "blank" multiple times, so that it skips the next section of code if a sheet won't activate. It will work the first time, then the second time it won't skip past the error, just presents it as if there was no If Error command at all. The code looks like this:
On Error GoTo errorHandler1
Windows(MyFile).Activate
Sheets(secondDetailTab).Activate
'bunch of code
errorHandler1:
'bunch of code
On Error GoTo errorHandler2
Windows(MyFile).Activate
Sheets(secondDetailTab).Activate
'bunch of code
errorHandler2:
'bunch of code
If Sheets(secondDetailTab).activate has an error the first time, it will skip to errorHandler1 like it's supposed to, but if there's an error the second time then it just presents the error normally and ignores the On Error GoTo errorHandler2 command. Any suggestions would be greatly appreciated!
You could do it indeed in the way as you want to do it although this is not best practise to do it like that. You need to reset the error with On Error Goto -1 before you "activate" another error handler.
On Error GoTo errorHandler1
Windows(myFile).Activate
Sheets(secondDetailTab).Activate
'bunch of code
errorHandler1:
'bunch of code
On Error GoTo -1
On Error GoTo errorHandler2
Windows(myFile).Activate
Sheets(secondDetailTab).Activate
'bunch of code
errorHandler2:
'bunch of code
Here you find a pretty good tutorial on error handling. There is also an explanation for On Error Goto -1
In the Remarks section of the On Error MS doc
If an error occurs while an error handler is active (between the occurrence of the error and a Resume, Exit Sub, Exit Function, or Exit Property statement), the current procedure's error handler can't handle the error.
My suggestion is that instead of stacking error handlers, you test and avoid errors in the main code. If Sheets(secondDetailTab).Activate is predictably causing errors then first test if the sheet exists and is valid. I wrote a short function that does that:
Function SheetExists(ByRef ExcelBook As Workbook, ByVal SheetName As String) As Boolean
Dim ws As Object
For Each ws In ExcelBook.Sheets
If ws.Name = SheetName Then Exit For
Next ws
SheetExists = Not ws Is Nothing
'True = it Exists, False = Doesn't Exist
End Function
Sub Example()
Const sName As String = "Sheet4"
Dim B As Boolean
B = SheetExists(ThisWorkbook, sName)
MsgBox B
End Sub
You could implement this into your code by changing Sheets(secondDetailTab).Activate into If SheetExists(WB, secondDetailTab) Then Sheets(secondDetailTab).Activate
Error handling in VBA is primitive compared to other languages. It is much better to check for error conditions preemptively than to catch errors after they occur. Check to see if files exist before you try to open them, don't assume the function you called succeeded--check the return value. If you practice these steps consistently, you almost never need to handle errors.
When you do need to handle errors, you can be very specific about the scope of the error trap and which errors you're handling. Here is a pattern I have found myself using a lot. You wrap a single line in the error handler, then check the error code. For example, I prefer Toddleson's code above, which checks to see if a worksheet exists by enumerating the existing worksheets, but this an alternative implementation which illustrates the use of tightly focused error handling.
‘ Get worksheet by name. If the worksheet does not exist, create a new worksheet.
Function GetWorksheet(ByVal name As String) As Worksheet
Dim ws As Worksheet
' Turn on error handling.
On Error GoTo Error_NoSuchSheet
Set ws = Worksheets(name)
' Turn off error handling.
On Error GoTo 0
' Create new worksheet.
If ws Is Nothing Then
Set ws = Worksheets.Add
ws.name = name
End If
Set GetWorksheet = ws
Exit Function
Error_NoSuchSheet:
If Err.Description = "Subscript out of range" Then
' Resume execution on the line following the one that threw the error.
Resume Next
Else
' Invoke the default VBA error handler.
Err.Raise Err.Number
End If
End Function
Note that only a specific error on one specific line is handled. An error on any other line will receive the default error handling (VBA pops up a dialog box). The same thing if any other error occurs on that one line. This pattern provides a much more finely granulated control of error conditions. This is not necessarily the best way to handle an error in every situation and it is far from the only way, but I think it has significant advantages over the pattern you described.
Of course any error handling, or no error handling at all, is significantly better than this.
Function IgnoreAllErors()
' Ignore all errors.
On Error Resume Next
' Your code here
End Function
Please don't do this.
I have a macro, and part of the code is concerned with:
1) detecting if a column contains empty cells - if so filling them with some value
2) detecting if a column contains cells containing errors (such as N/A) and if so filling them with some value
Now if there are no error/empty cells in the column, the line that finds them gives a "Run-time error 1004 no cells were found".
I use error handling to skip this with GoTo.
Below is the code - the first error handling GoTo works perfectly, while the second gives the expected error, although there is Error handling GoTo set, that does not seem to work. Code with comments:
On Error GoTo EErrorOne
'depending on file I get, below line will generate error and code successfully skips to ErrorOne label
Workbooks(nazwawb).Sheets(szitnr).Columns(ktorepole).SpecialCells (xlCellTypeBlanks)
' code to be skipped
Workbooks(nazwawb).Sheets(szitnr).Columns(ktorepole).Select
Selection.SpecialCells(xlCellTypeBlanks).Select
Selection.Value = "Some Value"
' end of code to be skipped
EErrorOne:
' successfully skipped error line by now. Below line should set a new Error handling procedure.
On Error GoTo EErrorTwo
Workbooks(nazwawb).Sheets(szitnr).Columns(ktorepole).Select
' Below line generates an error but does not skip to EErrorTwo label as detailed in the current Error handling procedure
Selection.SpecialCells(xlCellTypeFormulas, 16).Select
' code to be skipped
Selection.SpecialCells(xlCellTypeFormulas, 16).Select
Selection.Value = "Some Value"
' end of code to be skipped
EErrorTwo:
' Below line should reset error handling procedure to normal for subsequent handling of other errors:
On Error GoTo 0
It seems that error handling procedure (GoTo specific label) is ignored, and instead, an error message is shown as if the error handling was reset to GoTo 0. How do I skip the second error?
You're not clearing your errors when they occur, just trying to jump over them and the code is wondering what's going on with the error.
As Chip Pearson says on his site:
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
and continues with
The Resume statement instructs VBA to resume execution at a specified
point in the code. You can use Resume only in an error handling
block; any other use will cause an error. Moreover, Resume is the only
way, aside from exiting the procedure, to get out of an error handling
block. Do not use the Goto statement to direct code execution out of
an error handling block. Doing so will cause strange problems with
the error handlers.
http://www.cpearson.com/excel/errorhandling.htm
The ideal way is to avoid the error in the first place - check the workbook exists before opening it, check the sheet exists in the workbook before trying to reference it and if an error occurs jump out of the main body of the routine, deal with the error and then jump back in again.
As an example:
Sub Test()
On Error GoTo ERR_HANDLE
'.... code ....
FAST_EXIT:
'Code to tidy up, clear references etc before finishing.
Exit Sub
ERR_HANDLE:
Select Case Err.Number
Case 1004
'Code to resolve error
'.
'.
'Resume - clear error, jump back to line that caused it.
'Resume Next - clear error, jump to line after the one that caused error.
'Resume FAST_EXIT - clear error and go to line label.
Case 92 'Do something else to resolve error.
Case Else
End Select
End Sub
Inside error handling routines, it seems as though defining new error handling routines won't work, unless you clear the previously set error routine (https://excelmacromastery.com/vba-error-handling/):
'...
EErrorOne:
On Error GoTo -1 'Clears error trap flag
On Error GoTo EErrorTwo 'Reset error handling
'...
Edit after accepted:
As was discussed in the comments, On Error GoTo -1 clears the error trap flag, while Err.Clear only clears the error.
The code below illustrates this by creating two errors and trying to trap them.
On Error Goto -1 allows the second error to be trapped by the On Error GoTo NextLabel line and the code jumps to the label when the error occurs.
Err.Clear keeps the first error live, so when the second error occurs the error message is displayed rather than the code jumping to the label.
Sub ClearErr()
Dim x As Long
Dim y As Worksheet
'Jump to label when "Division by 0" occurs.
On Error GoTo ErrorLabel
x = 1 / 0
Debug.Print x
ErrorLabel:
'On Error GoTo -1 'Next error is trapped.
Err.Clear 'Untrapped error on y=Worksheets(1)
'Jump to label when "Object Variable not set" occurs.
On Error GoTo NextLabel
y = Worksheets(1)
Debug.Print y.Name
NextLabel:
End Sub
I have looked online as much as I could (except for the Microsoft support website, which is blocked at work for some reason). I am trying to simply skip an error. My code written here is simplified but should work the same way.
What my code is supposed to do:
One of my subs creates shapes in a loop and names them (btn_1, btn_2, etc). But before creating them, it calls a sub that tries to delete them so as not to create duplicates. This sub loops through (btn_1, btn_2, etc) and deletes the shapes using:
for i = 1 to (a certain number)
Set shp = f_overview.Shapes("btn_" & i)
shp.delete
next
Of course, it happens that the shape cannot be deleted because it simply does not exist. I have found that most of the time, the reccomended fix is to add (on error resume next) before setting the shape, as I get an error saying it does not exist. I have tried it inside the loop, before the loop, etc, like so:
for i = 1 to (a certain number)
On Error Resume Next
Set shp = f_overview.Shapes("btn_" & i)
shp.delete
next
As far as I understand it is supposed to loop right through if the shape doesn't exist, but I still get the same error whether or not I add the On error resume next! What am I doing wrong?
EDIT: There is no error when the shapes do exist.
I have found that most of the time, the reccomended fix is to add (on error resume next) before setting the shape, as I get an error saying it does not exist.
NO!
The recommended way to handle runtime errors is not to shove them under the carpet and continue execution as if nothing happened - which is exactly what On Error Resume Next does.
The simplest way to avoid runtime errors is to check for error conditions, and avoid executing code that results in 100% failure rate, like trying to run a method on an object reference that's Nothing:
For i = 1 To (a certain number)
Set shp = f_overview.Shapes("btn_" & i)
If Not shp Is Nothing Then shp.Delete
Next
In cases where you can't check for error conditions and must handle errors, the recommended way is to handle them:
Private Sub DoSomething()
On Error GoTo CleanFail
'...code...
CleanExit:
'cleanup code here
Exit Sub
CleanFail:
If Err.Number = 9 Then 'subscript out of range
Err.Clear
Resume Next
Else
MsgBox Err.Description
Resume CleanExit
End If
End Sub
There is nothing WRONG in using OERN (On Error Resume Next) provided you understand what you are doing and how it is going to affect your code.
In your case it is perfectly normal to use OERN
Dim shp As Shape
For i = 1 To (a certain number)
On Error Resume Next
Set shp = f_overview.Shapes("btn_" & i)
shp.Delete
On Error GoTo 0
Next
At the same time ensure that you don't do something like
On Error Resume Next
<Your Entire Procedure>
On Error GoTo 0
This will suppress ALL errors. Use proper error handling as shown by Matt
Edit:
Here is another beautiful example on how to use OERN This function checks if a particular worksheet exists or not.
Function DoesWSExist(wsName As String) As Boolean
Dim ws As Worksheet
On Error Resume Next
Set ws = ThisWorkbook.Sheets(wsName)
On Error GoTo 0
If Not ws Is Nothing Then DoesWSExist = True
End Function
If you wish you can also loop through all the sheets to check is the sheet exists or not!
Instead of trying to blindly delete shapes and skipping errors, why not run through the list of known shapes and delete them. Then you don't have to worry about an On Error Resume Next which often ends up being abused.
Sub Test(TheSheet As Worksheet)
Dim Shp as Shape
For Each Shp in TheSheet.Shapes
If left(Shp.Name, 4) = "btn_" Then
Shp.Delete
End if
Next
End Sub
If you want to delete all shapes, remove the If statement. If you want to delete a number of differently named shapes, modify the If statement appropriately.
It sounds like you have the wrong error trapping option set. Within the VBA Editor, Select Tools -> Options. In the window that opens, select the General tab, and pick the Break on Unhandled Errors radio button. This should allow Excel to properly process the On Error Resume Next command.
I suspect that you have Break on All Errors selected.
Try:
On Error Resume Next
for i = 1 to (a certain number)
Set shp = f_overview.Shapes("btn_" & i)
if err<>0 then err.clear else shp.delete
next
on Error Goto 0
How would I do an inline error handling routine in VBA? I don't want to put the error handler at the end.
This is from CPearson's Error Handling in VBA
Sub testErrHandling()
On Error GoTo ErrHandler:
Debug.print 9 / 0 'divide by zero error
Worksheets("NewSheet").Activate 'missing worksheet error
'more code here
Exit Sub
ErrHandler:
If Err.Number = 9 Then
' sheet does not exist, so create it
Worksheets.Add.Name = "NewSheet"
' go back to the line of code that caused the problem
Resume
End If
End Sub
But I'm looking for something more like a Try/Catch block in VB.net
This code will handle the error inline. This is a very cleanly structured pattern for handling an error. The flow moves very cleanly from top to bottom; no spaghetti code here.
VBA is an old language and has limitations. One of the ways to use error handling is to use Goto statements in the form of On Error Goto <Label> and Resume <Label>. This creates an opportunity.
Traditionally the error handler is placed at the bottom. But with the advances made in VB.net, it seems reasonable to leverage ideas to improve code. Try/Catch is a very structured way of handling errors and is very easy to follow. This pattern attempts to reproduce that in a very clean concise way. The flow is very consistent and doesn't jump from place to place.
Sub InLineErrorHandling()
'code without error handling
BeginTry1:
'activate inline error handler
On Error GoTo ErrHandler1
'code block that may result in an error
Dim a As String: a = "Abc"
Dim c As Integer: c = a 'type mismatch
ErrHandler1:
'handle the error
If Err.Number <> 0 Then
'the error handler is now active
Debug.Print (Err.Description)
End If
'disable previous error handler (VERY IMPORTANT)
On Error GoTo 0
'exit the error handler
Resume EndTry1
EndTry1:
'more code with or without error handling
End Sub
Sources:
Pearson Error Handling In VBA
How to: Handle Run-Time Errors in VBA
Properly Handling Errors in VBA (Excel)
Properly managed this works quite nicely. It is a very clean flowing pattern that is reproducible anywhere it is needed.
You can try assigning your object in a variable and use On Error Resume Next instead.
Dim sh As Worksheet
'This is essentially the "Try" part
On Error Resume Next 'this ignores the error
Set sh = Worksheets("NewSheet")
On Error Goto 0 'this resets the active error handling routine
'Then this is the "Catch" part I guess
If sh Is Nothing Then 'check is something is assigned to sh
'And I think this is "Finally" part
Set sh = Worksheets.Add: sh.Name = "NewSheet" 'add otherwise
End If
Not really familiar with the Try/Catch since I've not done some VB.Net but this is the closest inline error correction I can think of for your example. HTH.
After months of insecurity/confusion about some obscure laws of VBA error handling, never fully satisfied by any of the popular documentation pages (Microsoft VBA/VB, C. Pearson and a others), I dedicated some days of trial and error to reconstruct the complete set of the (written and unwritten/corrected) rules governing VBA error handling:
LEGEND:
1A: On Error Goto 0. 1B: On Error GoTo line/label. 1C: On Error Resume Next
2A: an error occurs. 2B: On Error GoTo -1
3: being in a subprocedure, temporarily, just until coming back
4: Erl
5: Resume, Resume Next, Resume line/label
6: Exit […], End […]
RULES:
With 1A in effect*, error handling is and stays disabled and inactive. This is the default.
With 1B in effect*, error handling is initially enabled and inactive; it's disabled while 3 and by 2A or 6, and it's activated on 2A and inactivated while 3 and by 5 (that also reenable it) or 6.
With 1C in effect*, error handling is initially enabled and inactive; it's disabled only while 3 and by 6 (it's not disabled by 2A!), and it always stays inactive (supposedly activated and inactivated immediately on 2A).
*= If 1A, 1B, 1C are called while error handling is active, the Err object is cleared immediately but the On Error action change effect is delayed until error handling is inactivated (by 5 or 6).
2B (instantaneously) inactivates error handling (if any); error handling returns(1B)/stays(1C) enabled.
1A, 1B, 1C, 2B, 5, 6 also instantaneously clears the Err object.
If an error occurs while in a subprocedure, if unhandled (disabled) in the current subprocedure it's passed to the first calling (parent) procedure where it's enabled and inactive. If no one is found, it stays there (in the subproc.).
4 if error handling is active returns the line (if specified as a number label) of last error, otherwise 0.
5 (correctly) crashes (err 21: "Resume without error") if called while error handling is inactive.
With all these rules fresh in mind, and starting from the D_Bester's solution posted here, here's my revised solution correcting a couple of errors/inefficiences in his code:
Sub InLineErrorHandling()
'code without error handling
On Error GoTo ErrHandler1 'enable error handler
'code block that may result in an error
Dim a As String: a = "Abc"
Dim c As Integer: c = a 'type mismatch
'inline error handler routine
ErrHandler1:
If Err.Number <> 0 Then
Debug.Print err.Description
On Error GoTo -1 ' inactivate error handler
End If
On Error GoTo 0 'disable error handler
'more code without error handling (default mode)
Err.Raise 123
End Sub
... where if you want to avoid using the undocumented On Error Goto -1 you can change the error handling routine this way:
ErrHandler1: If Err.Number <> 0 Then
Debug.Print err.Description
Resume ErrHandler1end 'inactivate & exit error handler
End If
ErrHandler1end: On Error GoTo 0 'disable error handler
... and if no errors are expected in the error handling routine, can be further reduced to this (very standard) alternative:
Sub InLineErrorHandling()
'code without error handling
On Error Resume Next 'enable error handler
'code block that may result in an error
Dim a As String: a = "Abc"
Dim c As Integer: c = a 'type mismatch
'inline error handler routine
If Err.Number <> 0 Then
Debug.Print err.Description
End If
On Error GoTo 0 'disable error handler
'more code without error handling (default mode)
err.Raise 123
End Sub
It also worth mentioning that, in both the choices, if we also want to know what line (first for 1B, last for 1C) of the "code block that may result in an error" resulted in an error, we can use the Erl function, like this:
...
'code block that may result in an error
10 Dim a As String: a = "Abc"
20 Dim c As Integer: c = a 'type mismatch
'inline error handler routine
If Err.Number <> 0 Then
Debug.Print "Error """ & err.Description & """ in line " & Err
...