Problem with successive "On Error" statements

grahamj

Board Regular
Joined
Feb 18, 2003
Messages
101
I find that if I use "On Error" statements two or three times in a programs to control the flow, that I get a Debug message (like "Subscript out of range") instead of the program obeying the GoTo.

Is there some kind of limitation, or some kind of reset that I didn't do?

I'm using Excel 2000.

Example:
Code:
  blnTemplate = False
  On Error GoTo TemplateNotFound
  If Len(Sheets("Template").Name) > 0 Then
    blnTemplate = True
  End If

  strNewTab = Format(dteDaily, "ddmmmyyyy")
  On Error GoTo TabNotFound
  If Len(Sheets(strNewTab).Name) > 0 Then
    wbkReport.Worksheets(strNewTab).Select
    Response = MsgBox("The report for " & _
      Format(dteDaily, "dd mmm yyyy") & _
      " already exists." & Chr(10) & _
      "Do you want to overwrite it?", _
      vbYesNo + vbDefaultButton2, "Daily report")
    If Response = vbYes Then
      Application.DisplayAlerts = False
      wbkReport.Worksheets(strNewTab).Delete
      Application.DisplayAlerts = True
    Else
      Exit Sub
    End If
  End If
TabNotFound:
  On Error GoTo 0
  wbkReport.Sheets("Template").Copy Before:=Sheets(1)
  Set shtReport = ActiveSheet
  shtReport.Name = strNewTab

TemplateNotFound:

  If Not blnTemplate then
    Err.Clear
    On Error Resume Next
    Set shtReport = wbkReport.Sheets(strReportSheet)
    If Err.Number = 9 Then 'worksheet not found
      MsgBox " . . ."
      Exit Sub
    End If
    On Error GoTo 0
  End If
If it jumps to TemplateNotFound (following the first "On Error GoTo") it fails on the Set shtReport.

In this case it fails to follow the "On Error Resume". In other cases it has failed on a GoTo testing the length of the sheet name.

Thanks
 

Excel Facts

Which came first: VisiCalc or Lotus 1-2-3?
Dan Bricklin and Bob Frankston debuted VisiCalc in 1979 as a Visible Calculator. Lotus 1-2-3 debuted in the early 1980's, from Mitch Kapor.
In general, if you want the code to continue after the line that caused the error then you would put "Resume" in the portion of the code that has the error handler.

I looked at what your code was doing and I think you may have a hard time eventually with that code. There are easier ways to do things. (well, easier in my opinion). For example, you check to see if two worksheets exist in your code. This is pretty redundant and you are introducing complications to your subroutine. The way I usually handle something like this is to create a separate function or subrtoutine to handle code that will be executed more than. In this case, I created a function called "IsSheet". So to check if a sheet exists, I simple put this line in the code:

Code:
  If IsSheet("Template") = True Then
     '...your code here

Anyway, using this approach I believe that your code has been simplified to just a few if statements. Have a look and see if this achieves what you want.

Code:
Public Sub Example()
  
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'  I added these variable declarations just to make it work
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  Dim blnTemplate As Boolean
  Dim dteDaily As Date
  Dim strNewTab As String
  Dim wbkReport As Workbook
  Dim response As String
  Dim shtReport As Worksheet
  Dim strReportSheet As Worksheet
  
'++++++++++++++++++++++++++++++++++++++++++++++++++
' This should be the only code that you need
'++++++++++++++++++++++++++++++++++++++++++++++++++
  If IsSheet("Template") = False Then
    MsgBox " . . ."
  End If

  strNewTab = Format(dteDaily, "ddmmmyyyy")
  
  
  If IsSheet(strNewTab) = True Then
    wbkReport.Worksheets(strNewTab).Select
    response = MsgBox("The report for " & _
      Format(dteDaily, "dd mmm yyyy") & _
      " already exists." & Chr(10) & _
      "Do you want to overwrite it?", _
      vbYesNo + vbDefaultButton2, "Daily report")
    If response = vbYes Then
      Application.DisplayAlerts = False
      wbkReport.Worksheets(strNewTab).Delete
      Application.DisplayAlerts = True
    Else
      Exit Sub
    End If
  Else
    wbkReport.Sheets("Template").Copy Before:=Sheets(1)
    Set shtReport = ActiveSheet
    shtReport.Name = strNewTab
  End If

End Sub
Private Function IsSheet(ByVal sSheet As String) As Boolean

  Dim oWorkSheet As Worksheet
  'If there's an error setting the sheet, then it doesn't exist
  On Error GoTo ERR_DOES_NOT_EXIST
  Set oWorkSheet = Sheets(sSheet)
    IsSheet = True
  Exit Function
    
ERR_DOES_NOT_EXIST:
  IsSheet = False

End Function
 
Upvote 0
Thanks very much, Mark.

I've used your IsSheet function and it makes the code work beautifully, probably by separating the "On Error" statements from each other.

I did not use your simplified code because it simplified-out some of the complications that I need. I was using an example of succesive "On Error" statements and didn't overly explain what the code was attempting.

Prior to this code, the user opens a "Daily Report" for values to be added into from other spreadsheets. There are two versions of daily report. One has monthly sheets (already existing), with separate columns for each day. The other has daily sheets, which are created from a template sheet each time the report is run.

So the code first checks which kind of report it is, by looking for the Template sheet. If the template exists, it then checks if the report has already been run for this date (so it looks for the date sheet). If the report exists it gives the option of overwriting it or of cancelling the job. If the user chooses to overwrite, it just deletes the sheet. Then, whether the report existed or not, it creates a new sheet from the template.

On the other hand, if there is no Template, the code looks for the monthly sheet (strReportSheet) which should exist. If it can't find it, it puts out a message and exits.

See why I didn't go into that explanation in the first place? It would have diverted attention from the main question, which was Why does it fail on the second "On Error", instead of following the GoTo (or Resume)?

And, although we now have a workaround which makes this particular code work, the question remains unanswered.

It happens in other code (where I do equally as convoluted logic) and I normally resort to a workaround like "For each ws in Worksheets".

In this case I have kept the "On Error Resume Next" and "Set shtReport ..." and it has become the first "On Error" statement and so it works.

It is just that when I have occasion to use "On Error" more than once and it follows the first GoTo, it always seems to give me the Debug pop-up on the second "On Error" bit. That is, it doesn't obey the GoTo or Resume, it acts as though no error trapping was specified. And I'd like to know why.

I have used Err.Clear (because it is still set to 9 after the first one), and I have tried "On Error GoTo 0" before setting it somewhere else.

Thanks again for the workaround, but is there another reset command I should be using in this case? Or is it a bug? Or is there a restriction on the "On Error" statement that I shouldn't even be trying to do what I was trying to do?
 
Upvote 0
Hi Graham,

The key part of Mark's explanation was in the first sentence :
Mark O'Brien said:
In general, if you want the code to continue after the line that caused the error then you would put "Resume" in the portion of the code that has the error handler.
Think of it like this : Excel has two states, normal and error. When an error arises Excel enters the 'error' state. If you use custom error-handling (On Error GoTo ..) you must reset Excel back to the 'normal' state before using any further custom error-handling. This reset is achieved by use of "Resume" (Err.Clear does not have the same effect).

HTH
 
Upvote 0
Hope you don't mind me jumping in but I would like to learn abit more about errors. If err.clear doesn't return Xl to normal state what does it do? Does On error goto 0 return Xl to normal? The use of resume...does it need to include next or can you have an error trap with the line resume by itself? Dave
 
Upvote 0
Thanks, Richie. I think I'm beginning to understand.

A lot of "On Error" stuff that I come across goes to an error handler at the bottom of the code and then just quietly exits, so it doesn't need to be reset. But if I want to continue on I need to use a "Resume" somewhere.

That makes it a bit tricky if I use "On Error Resume Next". How is that generally supposed to be used? Just to test and then exit from the program again?

I can see I need to do a little bit of recasting of my code.
The IsSheet function worked because the error occurred within the function and is presumably reset when the function ends.

Thanks. I'll do a bit of experimentation.
 
Upvote 0
a quote from the MS VBA help as follows...
The Err object's properties are reset to zero or zero-length strings ("") after an Exit Sub, Exit Function, Exit Property or Resume Next statement within an error-handling routine. Using any form of the Resume statement outside of an error-handling routine will not reset the Err object's properties. The Clear method can be used to explicitly reset Err.
I'm again not understanding XL and errors. Somewhat important stuff I think. GrahamJ...this has been a very informative read so I thought I would continue on. Hope this helps you learn some stuff too. Dave
edit: Richie(UK) already helped me here. Might also help
http://www.mrexcel.com/board2/viewtopic.php?t=46602&highlight=error
 
Upvote 0
Hi Graham and Nd,

OK, I'll offer a little more detail about the way that error-handling operates (as I understand it, anyway ;) ).

Custom error-handling
We invoke our own custom error-handling by use of the On Error ... statement (you really should read the VBE Help on this subject as its quite good). We return contol of error-handling to Excel by use of the On Error GoTo 0 statement.

On Error Resume Next
We use this when we want Excel to ignore any errors that arise. That is, On an Error arising code execution will Resume on the Next line of code.

This should be used when you anticipate that an error may arise at a particular point in your code but you do not want the error to be fatal (that is, to halt code execution). A common use of this approach is when setting object variables. We then test whether the object is Nothing to determine the success of the assignment. For example:
Code:
Sub Oops1()
    Dim ws As Worksheet
    
    On Error Resume Next
    'invoke custom error-handling - continue to line after error-causing line
    Set ws = ThisWorkbook.Worksheets("Nosuchsheet")
    'cause an error
    On Error GoTo 0
    'return error-handling to Excel
    
    If ws Is Nothing Then
        MsgBox "Worksheet not found"
    Else
        MsgBox "Found it!"
    End If
    'test whether or not the ws variable was set
    
End Sub
Note how error-handling is passed back to Excel immediately after the line that we suspect may cause an error. I would not advocate the approach adopted by those whose idea of error-handling is to simply add an 'On Error Resume Next' at the start of their routine and never return contol to Excel. Sure, this will suppress the error messages but its unlikely that your code will continue to execute as intended.

On Error GoTo ...
The alternative to simply continuing code execution is to jump to some error-handling code.

This code is best placed at the end of your Sub or Function and should be preceded by an 'Exit Sub' instruction to make sure that the code isn't executed automatically whenever the end of the procedure is reached.

Once Excel has entered the Error state, to continue the analogy used above, it must be reset. This is achieved by use of the Resume statement. You can Resume (return to the error line), Resume Next (return the line after the error line), or Resume ... (specify where the code should resume from).

In the example below, the use of Resume ensures that the second error is also processed.
Code:
Sub Oops2()
    Dim ws As Worksheet
    
    On Error GoTo errhandler
    'invoke custom error-handling - go to our specified line
    Set ws = ThisWorkbook.Worksheets("Nosuchsheet")
    'cause an error
    Set ws = ThisWorkbook.Worksheets("Nosuchsheeteither")
    'cause another error
    On Error GoTo 0
    'return error-handling to Excel
    
    MsgBox "Finished"
    Exit Sub
    'ensure error-handler isn't processed at the end of the routine
errhandler:
    MsgBox "An error has arisen : " & Err.Number & vbNewLine & Err.Description
    'advise user of the error
    Resume Next
    'return code execution to the line after the error-causing line
    
End Sub
This is in contrast to the situation below where Err.Clear is used and then code execution is passed back to the main routine via a GoTo statement. The Err properties are cleared, as demonstrated by the Messagebox, but the second error is still fatal.
Code:
Sub Oops3()
    Dim ws As Worksheet, bSecond As Boolean
    
    On Error GoTo errhandler
    'invoke custom error-handling - go to our specified line
    Set ws = ThisWorkbook.Worksheets("Nosuchsheet")
    'cause an error
seconderror:
    bSecond = True
    'indicate this is the second part
    Set ws = ThisWorkbook.Worksheets("Nosuchsheeteither")
    'cause another error
    On Error GoTo 0
    'return error-handling to Excel
    
theend:
    MsgBox "Finished"
    Exit Sub
    'ensure error-handler isn't processed at the end of the routine
errhandler:
    MsgBox "An error has arisen : " & Err.Number & vbNewLine & Err.Description
    'advise user of the error
    Err.Clear
    'clear the err properties
    MsgBox "An error has arisen : " & Err.Number & vbNewLine & Err.Description
    'confirmation that the properties are cleared
    If bSecond = True Then Resume theend
    GoTo seconderror
    'the Clear instruction will not have reset the error-checking,
    'we have used GoTo rather than Resume,
    'the second error will be fatal
    
End Sub
Another common mistake is to add a further On Error instruction within the error-handler itself without having reset the first error. This is shown in the following example:
Code:
Sub Oops5()
    Dim ws As Worksheet
    
    On Error GoTo errhandler
    'invoke custom error-handling - go to our specified line
    Set ws = ThisWorkbook.Worksheets("Nosuchsheet")
    'cause an error
    On Error GoTo 0
    'return error-handling to Excel

theend:
    MsgBox "Finished"
    Exit Sub
    'ensure error-handler isn't processed at the end of the routine
errhandler:
    MsgBox "An error has arisen : " & Err.Number & vbNewLine & Err.Description
    'advise user of the error
    On Error GoTo errhandler2
    'error-handler within the error-handler
    Set ws = ThisWorkbook.Worksheets("Nosuchsheeteither")
    'cause another error
    'the first error has not been reset - the second handler will not be invoked
    GoTo theend
errhandler2:
    MsgBox "An error has arisen : " & Err.Number & vbNewLine & Err.Description
    'advise user of the error
    Resume theend
    
End Sub
We could achieve this objective by using something like this:
Code:
Sub Oops6()
    Dim ws As Worksheet
    
    On Error GoTo errhandler
    'invoke custom error-handling - go to our specified line
    Set ws = ThisWorkbook.Worksheets("Nosuchsheet")
    'cause an error
    On Error GoTo 0
    'return error-handling to Excel

theend:
    MsgBox "Finished"
    Exit Sub
    'ensure error-handler isn't processed at the end of the routine
errhandler:
    MsgBox "An error has arisen : " & Err.Number & vbNewLine & Err.Description
    'advise user of the error
    Resume reset
reset:
    On Error GoTo errhandler2
    'error-handler within the error-handler
    Set ws = ThisWorkbook.Worksheets("Nosuchsheeteither")
    'cause another error
    GoTo theend
errhandler2:
    MsgBox "An error has arisen : " & Err.Number & vbNewLine & Err.Description
    'advise user of the error
    Resume theend
    
End Sub
However, in my opinion, this approach is starting to resemble 'spaghetti' code - the flow of the code is becoming increasingly difficult to follow. It would be preferable to have just the one error-handler that is capable of handling a variety of errors.

Generic error-handler
If you have a larger project then, rather than adding detailed error-handling code to each and every Sub or Function, you may wish to consider using a generic error-handler.

This takes the form of a Sub or Function that is called by the routine that an error has arisen in. The routine passes details of the error and the procedure in which the error has arisen. The details can be written to a text file for later inspection.

The following is an example of how the error-handling is invoked in the Sub and of a typical generic error-handler. This stores the info in a text file, displays a message to the user, and gets a response from the user.
Code:
Sub TestErrorHandler()
    
    On Error GoTo ErrorHandler
    '
    Kill "Anon-existantfilename"
    '
    
ExitRoutine:
    Exit Sub
ErrorHandler:
    Select Case ProcessError("modErrorHandler : TestErrorHandler", Err.Number)
        Case Is = True: Resume
        'try again
        Case Is = False: Resume ExitRoutine
        'give up
    End Select
    
End Sub

Function ProcessError(strProc As String, iErrNo As Integer) As Boolean
'use info from calling procedure to produce an error log and user message

    Dim strMessage As String
    Dim intStyle As Integer
    Dim iFile As Integer
    
    Const strTitle As String = "Warning : Error"
    Const strMsg1 As String = "The following error has occurred:"
    Const strMsg2 As String = "Do you wish to retry?"
    
    strMessage = vbNewLine & "Error number : " & iErrNo & _
        vbNewLine & "Error description : " & Error(Err.Number) & _
        vbNewLine & "Procedure : " & strProc & vbNewLine
    'the basic error info
    
    iFile = FreeFile()
    Open ThisWorkbook.Path & "\RPS Add-in_Error.Log" For Append As #iFile
    Print #iFile, Now, ThisWorkbook.Name, strMessage
    Close #iFile
    'add the error message to the error log
    
    strMessage = strMsg1 & vbNewLine & strMessage & vbNewLine & strMsg2
    intStyle = vbExclamation + vbRetryCancel
    'the full error message
    
    ProcessError = _
        (MsgBox(prompt:=strMessage, Buttons:=intStyle, Title:=strTitle)) = vbRetry
    'display warning to user and get response (Retry or Cancel)
    
End Function
I have to thank XL-Dennis for first recommending this approach to me. I should also credit Mssrs Getz and Gilbert who authored the book "VBA Developers Handbook" which included details of the approach.

I hope that helps to clarify things a little.
 
Upvote 0
Richie..Thank you very much for your time and extensive efforts. I'm sure that I will refer to this post often. grahamj...I hope this has been beneficial for you as well. Dave
 
Upvote 0

Forum statistics

Threads
1,223,912
Messages
6,175,340
Members
452,638
Latest member
Oluwabukunmi

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top