On Error Statement (Visual Basic)
Enables an error-handling routine and specifies the location of the routine within a procedure; can also be used to disable an error-handling routine.
Without an On Error statement, any run-time error that occurs is fatal: an error message is displayed, and execution stops.
Whenever possible, we suggest you use structured exception handling in your code, rather than resorting to unstructured exception handling and the On Error statement. For more information, see Structured Exception Handling in Visual Basic.
On Error { GoTo [ line | 0 | -1 ] | Resume Next }
- GoTo line
-
Enables the error-handling routine that starts at the line specified in the required line argument. The line argument is any line label or line number. If a run-time error occurs, control branches to the specified line, making the error handler active. The specified line must be in the same procedure as the On Error statement, or a compile-time error will occur.
- GoTo 0
-
Disables enabled error handler in the current procedure and resets it to Nothing.
- GoTo -1
-
Disables enabled exception in the current procedure and resets it to Nothing.
- Resume Next
-
Specifies that when a run-time error occurs, control goes to the statement immediately following the statement where the error occurred, and execution continues from that point. Use this form rather than On Error GoTo when accessing objects.
An "enabled" error handler is one that is turned on by an On Error statement. An "active" error handler is an enabled handler that is in the process of handling an error.
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 cannot handle the error. Control returns to the calling procedure.
If the calling procedure has an enabled error handler, it is activated to handle the error. If the calling procedure's error handler is also active, control passes back through previous calling procedures until an enabled, but inactive, error handler is found. If no such error handler is found, the error is fatal at the point at which it actually occurred.
Each time the error handler passes control back to a calling procedure, that procedure becomes the current procedure. Once an error is handled by an error handler in any procedure, execution resumes in the current procedure at the point designated by the Resume statement.
Note |
|---|
| An error-handling routine is not a Sub procedure or a Function procedure. It is a section of code marked by a line label or a line number. |
Number Property
Error-handling routines rely on the value in the Number property of the Err object to determine the cause of the error. The routine should test or save relevant property values in the Err object before any other error can occur or before a procedure that might cause an error is called. The property values in the Err object reflect only the most recent error. The error message associated with Err.Number is contained in Err.Description.
Throw Statement
An error that is raised with the Err.Raise method sets the Exception property to a newly created instance of the Exception class. In order to support the raising of exceptions of derived exception types, a Throw statement is supported in the language. This takes a single parameter that is the exception instance to be thrown. The following example shows how these features can be used with the existing exception handling support:
On Error GoTo Handler
Throw New DivideByZeroException()
Handler:
If (TypeOf Err.GetException() Is DivideByZeroException) Then
' Code for handling the error is entered here.
End If
Notice that the On Error GoTo statement traps all errors, regardless of the exception class.
On Error Resume Next
On Error Resume Next causes execution to continue with the statement immediately following the statement that caused the run-time error, or with the statement immediately following the most recent call out of the procedure containing the On Error Resume Next statement. This statement allows execution to continue despite a run-time error. You can place the error-handling routine where the error would occur rather than transferring control to another location within the procedure. An On Error Resume Next statement becomes inactive when another procedure is called, so you should execute an On Error Resume Next statement in each called routine if you want inline error handling within that routine.
Note |
|---|
| The On Error Resume Next construct may be preferable to On Error GoTo when handling errors generated during access to other objects. Checking Err after each interaction with an object removes ambiguity about which object was accessed by the code. You can be sure which object placed the error code in Err.Number, as well as which object originally generated the error (the object specified in Err.Source). |
On Error GoTo 0
On Error GoTo 0 disables error handling in the current procedure. It doesn't specify line 0 as the start of the error-handling code, even if the procedure contains a line numbered 0. Without an On Error GoTo 0 statement, an error handler is automatically disabled when a procedure is exited.
On Error GoTo -1
On Error GoTo -1 disables the exception in the current procedure. It does not specify line -1 as the start of the error-handling code, even if the procedure contains a line numbered -1. Without an On Error GoTo -1 statement, an exception is automatically disabled when a procedure is exited.
To prevent error-handling code from running when no error has occurred, place an Exit Sub, Exit Function, or Exit Property statement immediately before the error-handling routine, as in the following fragment:
Public Sub InitializeMatrix(ByVal Var1 As Object, ByVal Var2 As Object) On Error GoTo ErrorHandler ' Insert code that might generate an error here Exit Sub ErrorHandler: ' Insert code to handle the error here Resume Next End Sub
Here, the error-handling code follows the Exit Sub statement and precedes the End Sub statement to separate it from the procedure flow. You can place error-handling code anywhere in a procedure.
Untrapped Errors
Untrapped errors in objects are returned to the controlling application when the object is running as an executable file. Within the development environment, untrapped errors are returned to the controlling application only if the proper options are set. See your host application's documentation for a description of which options should be set during debugging, how to set them, and whether the host can create classes.
If you create an object that accesses other objects, you should try to handle any unhandled errors they pass back. If you cannot, map the error codes in Err.Number to one of your own errors and then pass them back to the caller of your object. You should specify your error by adding your error code to the VbObjectError constant. For example, if your error code is 1052, assign it as follows:
Caution |
|---|
| System errors during calls to Windows dynamic-link libraries (DLLs) do not raise exceptions and cannot be trapped with Visual Basic error trapping. When calling DLL functions, you should check each return value for success or failure (according to the API specifications), and in the event of a failure, check the value in the Err object's LastDLLError property. |
This example first uses the On Error GoTo statement to specify the location of an error-handling routine within a procedure. In the example, an attempt to divide by zero generates error number 6. The error is handled in the error-handling routine, and control is then returned to the statement that caused the error. The On Error GoTo 0 statement turns off error trapping. Then the On Error Resume Next statement is used to defer error trapping so that the context for the error generated by the next statement can be known for certain. Note that Err.Clear is used to clear the Err object's properties after the error is handled.
Public Sub OnErrorDemo() On Error GoTo ErrorHandler ' Enable error-handling routine. Dim x As Integer = 32 Dim y As Integer = 0 Dim z As Integer z = x / y ' Creates a divide by zero error On Error GoTo 0 ' Turn off error trapping. On Error Resume Next ' Defer error trapping. z = x / y ' Creates a divide by zero error again If Err.Number = 6 Then ' Tell user what happened. Then clear the Err object. Dim Msg As String Msg = "There was an error attempting to divide by zero!" MsgBox(Msg, , "Divide by zero error") Err.Clear() ' Clear Err object fields. End If Exit Sub ' Exit to avoid handler. ErrorHandler: ' Error-handling routine. Select Case Err.Number ' Evaluate error number. Case 6 ' Divide by zero error MsgBox("You attempted to divide by zero!") ' Insert code to handle this error Case Else ' Insert code to handle other situations here... End Select Resume Next ' Resume execution at same line ' that caused the error. End Sub
Namespace: Microsoft.VisualBasic
Assembly: Visual Basic Runtime Library (in Microsoft.VisualBasic.dll)
Suppose you have the following code (standard way of handling errors):
'Loop starts (e.g. For, While, etc)
On Error Go To 100
'Risky line: this may fire an error:
Open filename For Input As #1
'An error is fire, so control is passed to line #100
'Do something - this code should not execute on error condition
...
200: 'continue
....
'Loop ends
Exit Sub ' or Function
100: 'Error code
'Do somethingelse - this code should execute on error condition
Go To 200
The above code will fire the error exception only once, and after that it will raise an unhandled error (e.g. ending the run).
If you want to keep executing the error code subsequently, the following modification is proposed:
On Error Resume Next
'Loop starts (e.g. For, While, etc)
'Risky line: this may fire an error:
Open filename For Input As #1
If Err.number =0 then 'no error was encountered; keep executing code
'Do something - this code should not execute on error condition
Else
'Do somethingelse - this code should execute on error condition
Err.clear
End If
....
200: 'continue: obsolete numbering now, but shown for clarity
'Loop ends
- 2/1/2012
- thanos67
If your code does not assign a value to Err.Number without actually raising an error (as mentioned in previous posts), because ErrObject's ("Err") default property is Err.Number, you can also avoid the error-handling subroutine by testing it this way:
Sub MySub()
Const ERR_MYERROR As Long = 1001
On Error Goto ErrHandler
[procedure code]
Err.Raise Number:=ERR_MYERROR, Description:="Something weird happened", Source:="VBAProject.MySub()"
ErrHandler:
If Err <> False Then 'False' Boolean constant = 0
Select Case Err.Number
Case ERR_MYERROR
[...]
Case Else
[...]
End Select
Err.Clear
End If
End Sub
Now this is great, but if ERR_MYERROR turns out being, say, a division by zero error you wish to handle in a clean fashion, we can assume there will be other uses for ERR_MYERROR somewhere else in the code. And then the whole Select Case logic will have to be repeated pretty much in every single procedure.
In one VBA project I managed to structure (/centralize) error handling by creating an "AppHandledException" class:
__________________________________________________________________________________________________
Private Type AppHandledExceptionDef
ErrorObject As ErrObject
ErrorInfo As String
End Type
Private This As AppHandledExceptionDef
'"This" refers to nothing more than a private local variable of the above defined type,
'that only exists in the instance of the AppHandledException class.
'Use "Me" to refer to the actual instance of the AppHandledException class.
Event OnError(ByRef ExceptionObj As AppHandledException)
Option Explicit
__________________________________________________________________________________________________
'All exposed properties are read-only:
Public Property Get ErrorInfo As String
ErrorInfo = This.ErrorInfo
End Property
__________________________________________________________________________________________________
'[This] "has-a" [ErrorObject] : expose the properties of This.ErrorObject we're interested in:
Public Property Get ErrorDescription As String
ErrorDescription = This.ErrorObject.Description
End Property
Public Property Get ErrorNumber As Long
ErrorNumber = This.ErrorObject.Number
End Property
Public Property Get ErrorSource As Long
ErrorSource = This.ErrorObject.Source
End Property
__________________________________________________________________________________________________
'Our AppHandledException class now needs a method to raise its event:
Public Sub Throw(ByRef ErrObj As ErrObject, Optional ByVal ErrInfo As String)
'ErrInfo argument is only supplied on "managed" errors.
'ErrObj ErrObject argument is supplied on all errors (VBA run-time and "logical" errors)
'AppHandledException events will throw an instance of the AppHandledException class;
'Set This.[member] values as per passed parameters:
This.ErrorObject = ErrObj
If Not IsMissing(ErrInfo) Then This.ErrorInfo = ErrInfo
RaiseEvent OnError(Me)
DoEvents
ErrObj.Clear
End Sub
__________________________________________________________________________________________________
'With a little tweaking you could consider using something like this, too:
Public Sub Warn(ByVal WarningMsg As String, Optional ByVal WarningInfo As String)
This.WarningMessage = WarningMsg
This.WarningInfo = WarningInfo
RaiseEvent OnWarning(Me)
DoEvents
End Sub
__________________________________________________________________________________________________
__________________________________________________________________________________________________
Using an instance of the AppHandledException class is quite straightforward: all exposed properties are read-only, which only leaves the "Throw" method to use. For total centralization of error handling, you would instanciate the class, using the WithEvents keyword, in the class module that defines your application object (let's call it clsApplication). Because you cannot use the New keyword along with WithEvents, you'll have to set the object in another statement; the Class_Initialize handler provides perhaps the most logical container for such a statement:
Public WithEvents AppErrorHandle As AppHandledException
Option Explicit
Private Sub Class_Initialize()
Set AppErrorHandler = New AppHandledException
End Sub
By declaring an object variable with the WithEvents keyword, the VBA IDE lists AppErrorHandler in the Object drop-down list (the one at the right, at the top of the class' code window), and the object's events in the Procedure drop-down list. Thus you create the event handler for your custom event just as you would for a ButtonClick() event on a form; you'll receive the AppHandledException object thrown by the error-handling subroutine that triggered the call:
Private Sub AppErrorHandle_OnError(ByRef AppException As AppHandledException)
[your application-wide error-handling code - log errors with a timestamp, for example]
End Sub
If you want to capture and reuse your own custom error codes, you'll probably want to use global constants in a standard module... But in a more OOP approach you would create a class (clsAppError) to hold the values (hard-coded or stored in a database) you want to pass to the Err.Raise method - error numbers and descriptions. Another class (clsAppErrors) would instanciate clsAppError as many times as there are errors you want to handle.
Then, event-handling subroutines can be standardized throughout your code: the handler handles both VBA runtime errors and your own app errors:
'Raise a handled/managed error:
With MyApp.AppErrors
Err.Raise Number:=.TestHandledError.Number, _
Description:=.TestHandledError.Description, _
Source:=ErrModule & ".MyProcedure(" & argValuesString & ")"
End With
ErrHandler:
If Err <> False Then
MyApp.AppErrorHandle.Throw Err 'if a VBA run-time error occurs, the handler will still pick it up.
End If
This structure is indeed quite heavy for a simple Excel macro, but for larger-scale Excel add-ins, it could be warranted, as it provides an application-level overhead on all error-handling, definitely beyond the typical Select Case error-handling subroutines scattered throughout the code. And nothing forbids having an AppWarningHandle object featuring a "Warn" method involving a "WarningMessage" property...
E:\2010\IPT\Choo Choo Train\Form1.vb(45): 'picTrain' is already declared as 'Private Dim picTrain() As System.Windows.Forms.PictureBox' in this class.
E:\2010\IPT\Choo Choo Train\Form1.vb(51): 'picTrain' is already declared as 'Friend Dim WithEvents picTrain As System.Windows.Forms.PictureBox' in this class.
can any1 tell me what they mean plz
- 3/11/2010
- Josh_Baum
Although removing the "Exit Sub" before the ErrorHandler label may work in most scenarios, it is not recommended.
Some methods may set Err.Number to a value other than 0 when no error is actually happening.
This is the reason why the error handling routine should only be executed "On Error", but not in normal case.
If you remove the "Exit Sub" before the ErrorHandler label, and your code execute a method that sets Err.Number to a value other than 0 (but doesn't actually raise an exception) just before entering the error handling routine, then your error handling routine will behave as if an exception was thrown when it was not the case!
This is an additional reason to move to structured exception handling using try... catch... finally
- 10/15/2008
- Michel Rotteleur
- 10/22/2008
- Thomas Lee
Note
Caution