Is there a simple way to check if a connection string was successful in connecting to the specified database:
Scenario:
Connection string example:
ConnString="DRIVER={MySQL ODBC 5.1 Driver}; SERVER=###.###.####.##; PORT=3306; DATABASE=DbName; USER=DbUser; PASSWORD=DbUser_Pswrd; OPTION=3;"
If the above fails, to logon for example, as the MySQL server is currently offline or whatever is there a simple way to check that there is an issue rather than the generic error 80004005 being thrown as explained below?
Solution to this is to solve error above being thrown when a page loads where the following line(s) in the code causes error as connection is not able to connect to the database:
rs.ActiveConnection = connString
I want to catch this connection issue before the above line is read so that I can redirect to a page that has no connection string and display a message saying the server is currently offline etc.
I just need the catch error code please.
VBScript Error Handling
The way to catch errors in VBScript is using the On Error Resume Next statement and Err object built into the VBScript Runtime.
What is the Err Object?
The Err object is used to contain information about errors raised at runtime, it always contains the last error raised, constantly overwriting itself as more errors are raised.
When errors are raised the execution is halted, so what is the point of Err?
This is because by default errors halt script execution. However VBScript provides a mechanism to ignore those errors and continue execution by moving to the next executable line and populate the Err object. We do this by placing a On Error Resume Next statement at the point where we want execution to ignore runtime errors.
It is important to remember that On Error Resume Next will only ignore runtime errors. Compilation Errors and Syntax Errors will still halt execution of a script.
So I'm using On Error Resume Next how do I trap an error?
With an If statement, once an error has been raised the Err object will be populated with that error and can be interrogated.
On Error Resume Next
'Line we are checking for error. When an error occurs execution will
'move to the next line, in this case the If Err.Number <> 0 Then.
rs.ActiveConnection = connString
If Err.Number <> 0 Then
'Raise a custom error, output error to screen or perform some action.
'Remember to clear the Err object before continuing.
Err.Clear
End If
'We no longer want to ignore errors
On Error Goto 0
Placing the On Error Resume Next statement resets the Err object as if the Err.Clear method had been called.
Common uses in Classic ASP
Sometimes you might want to detect a specific error and return a more friendly customised response, the simplest way to do this is building the response from the If Err.Number <> 0 Then statement.
If Err.Number <> 0 Then
Response.Write "Error (" & Err.Number & ") occurred: " & Err.Description
End If
A more complex approach is to raise your own error, which can then be captured by others further up the execution tree (depending on the complexity of your web application).
Err.Raise vbObjectError + 1, "My Custom App", "Connection not detected"
You might also want to capture a list of errors which can be done by using an Array or Scripting.Dictionary object instead of outputting the error immediately.
Dim errors(), error_count
Dim err_obj
...
'This block could be repeated throughout the code for various
'error checks.
If Err.Number <> 0 Then
ReDim Preserve errors(error_count)
errors(error_count) = Array(Err.Number, Err.Description)
error_count = error_count + 1
End If
...
'Output errors
If IsArray(errors) Then
Response.Write "<p>" & error_count & " error(s) have been detected</p>"
Response.Write "<ol>"
For Each err_obj In errors
Response.Write "<li>Error: " & err_obj(0) & " - " & err_obj(1) & "</li>"
Next
Response.Write "</ol>"
End If
Detecting various error codes that could be returned using a Select statement instead of using the classic If statement approach, like so;
Select Case Hex(Err.Number)
Case &H800A0006
'Overflow error
Case &H800A000B
'Division by zero error
Case Else
'Anything else
End Select
Useful Links
VBScript — Using error handling
Generating Sensible Error Messages Using Err.Raise
You would use on error resume next here, which basically tells your script to skip a line if it throws an error and continue from the next line, By checking if the err.number is not equal to 0, you can trap the error and do whatever you want from there. I would advise "disabling" on error resume next when you're done by using on error goto 0 as leaving it "enabled" throughout your code could lead to some undesirable results.
Here's a quick example of how your code would look:
err.clear
on error resume next
rs.ActiveConnection = connString
if err.number<>0 then
'## do whatever you want here
end if
on error goto 0
Related
In Access 2003-2016, I am using CurrentDb.Execute with dbFailonError to run an INSERT statement. The INSERT statement should fail (and it does) because one field has an incorrect value based on a related table with "Enforced Referential Integrity". However, it does not throw an error. I have tried recreating this issue in a new database, and the error works correctly. There is something wrong in the settings with my current database, and I don't want to recreate it from scratch. I have taken everything out of my database except for the problematic piece, and my minimal reproducible example database is at this link.
Here is my code, but the problem is that this code works fine and does throw errors when I create a new database from scratch. It just doesn't work in my current database.
Private Sub Command34_Click()
Dim testsql As String
testsql = "INSERT INTO tblObservations (Site,TotalDepth) VALUES ('SUD-096',5)"
With CurrentDb
On Error GoTo Err_Execute
.Execute testsql, dbFailOnError
On Error GoTo 0
MsgBox ("Upload completed. " & .RecordsAffected & " records added.")
End With
Exit Sub
Finish:
Exit Sub
Err_Execute:
If DBEngine.Errors.Count > 0 Then
For Each errLoop In DBEngine.Errors
MsgBox ("Error number: " & errLoop.Number & vbCr & errLoop.Description)
Next errLoop
End If
Resume Finish
End Sub
Use Option Explicit, like Hans said. Always use Option Explicit!
You're missing a reference to the Microsoft Office ##.# Access Database Engine object. This is where dbFailOnError is defined. Because you don't have that reference, dbFailOnError is not defined. This reference is added to all Access databases by default, and I strongly recommend adding it.
And because you're not using Option Explicit, VBA doesn't mind that it's undefined and just casts that undefined variable to a zero.
If, for some reason, you don't want to add the reference, use the corresponding value for dbFailOnError:
.Execute testsql, 128
We have an issue with an Access front-end that seemingly randomly but constantly times out users with no explanation. This has been a long-standing issue, each time the disconnect happens we're provided with the sub that's causing the issue and a generic error message.
I'm starting to think the error message is misleading, it gives a generic "Connection Failure" with an Error Code of 0. After Googling the code that apparently means no error occurred?
I use C# not VBA so I'm not familiar with it but the error handling is as follows:
Private Sub Form_Timer()
Dim blnSystemMaintenance As Boolean
On Error GoTo ErrHand
' Check for System Maintenance.
blnSystemMaintenance = DLookup("SystemMaintenance", "ConfigItems")
If blnSystemMaintenance Then
//Do stuff
Else
//Do stuff
End If
//Do stuff
End If
ErrExit:
Exit Sub
Resume
ErrHand:
' If Err.Number = 1 Then
' Else
MsgBox "Error number: " & Err.Number & vbCrLf & _
"Error description: " & Err.Description, vbCritical, _
"Unexpected Error Occurred in Sub: Form_Timer"
Resume ErrExit
' End If
End Sub
Is there a way to get a more helpful error message?
Domain aggregates often don't give descriptive error messages. I'm assuming your database runs into problems connecting to the backend when running the domain aggregate.
You can easily replace your domain aggregate with a recordset call:
blnSystemMaintenance = CurrentDb.OpenRecordset("SELECT SystemMaintenance FROM ConfigItems")(0).Value
Often, those give a bit more descriptive errors, if the error occurs at all when using a recordset.
Sometimes error messages may be misleading for some type of functions like DLookup, this is a system problem, but sometimes error messages are misleading because not all procedures have correct error handlers and it's difficult to localize the problem especially if the developer has no direct access to the application. So, correct error handlers may significantly simplify troubleshooting.
Normally I use two types of error handlers, which give me quite clear diagnostic messages. Most common - usual error handler for procedures, which may interact with the user and normally called by the system - events handlers:
Private Sub Frame2_Click()
On Error GoTo ErrorHandler
'code here
ExitHere:
On Error Resume Next
Exit Sub
Resume '>> remove in release
ErrorHandler:
MsgBox "Error " & err.Number & " (" & err.Description & "), Line " & Erl & " in procedure Frame2_Click of Form_Form1", vbExclamation + vbMsgBoxHelpButton, "Error " & Now()
Resume ExitHere
End Sub
The second type of handlers - for procedures, which may be called only by other VBA procedures, "bubble" handler, which adds information about the error to error description and passes the error up to caller procedure:
Sub MySub()
On Error GoTo ErrorHandler
'code here
ExitHere:
Exit Sub
Resume '>> remove in release
ErrorHandler:
Debug.Assert Not (STOP_AT_ERROR And IS_DEV) '>> remove in release
err.Raise err.Number, "MySub of Form_Form1", err.Description & vbCrLf & "in MySub of Form_Form1 at " & Erl
End Sub
It allows to show full call stack with error lines and procedure names. Line numbers should be added to each line of code though.
For debug simplifying I use two constants: if STOP_AT_ERROR is True and IS_DEV is True, code execution stops right after the error without bubbling and allows to inspect variables. For inspecting, I move the code execution pointer to the line Resume using Ctrl-F9, then press F8 and code pointer moves to the row, which caused the error.
In the production or QA versions just change IS_DEV to False and code won't stop at Debug.Assert line.
For automatic generating error handlers and adding line numbers, I use third party software, for instance, MZ-Tools. It can be done manually, but it's quite a time consuming
I have an error log that logs in the access table whenever a runtime error occurs for a user in the error trapper, and a particular error seems to occur for 10 random users, every hour at least.
This error appears to occur completely at random, on a random module with the Set ActiveForm code, with random users at random intervals. As far as I can see, there is no pattern between the users.
2475 - "You entered an expression that requires a form to be the active window".
This appears to occur in any of the modules that contain any of the setting of a form. I am using the following lines:
Dim af as Object
Set af = Screen.ActiveForm
I have tried using alternatives, such as declaring it as Form, and also tried the below:
Dim sstatus as String
Dim ps as String
If DLookup("[TM_UserType]", "[SD_Teams]", "[TM_username]= '" & usernm & "'") = "adj" Then
sstatus = "adj"
Else
sstatus = "tm"
End If
ps = "frmProdSubmit_" & sstatus
Then referencing the form this way:
Forms(ps).cmbTeam.Value = ""
But this still causes the same issue, even removing the ActiveForm part.
The last thing to mention (as I believe they could be factors) is that the front end is accessed through a shortcut, which minimises the Access window. Not sure if this could be the culprit, or if the user clicking another application can remove the focus.
The back-end of the database is also accessed by up to around 700 users each day.
As it stands, the error trapper pops up with the message, but the front end continues working correctly. It's just an annoying issue to resolve, but am slowly running out of ideas now, and any help would be hugely appreciated!
Error 2475 is thrown when a non-form object is the active screen object such as a table datasheet. I've encountered this error in an application that uses multiple instances of a form and needs to track whether the multiple form module is active or one of the other application module functions in which case all instances of the multiple forms (popups) need to have .visible set to false. I use the Screen.ActiveForm.Name call in the Form_Deactivate event.
You can trap the error in the procedure's error handler and take action knowing the screen's active object is not a form.
Example:
Private Sub Form_Deactivate()
On Error GoTo errHandler
If Screen.ActiveForm.Name <> "AnApplicationForm" Then 'throws 2475 if not a form object
sstatus = "status message"
End If
ExitSub:
Exit Sub
errHandler:
If Err.Number = 2475 Then Resume Next 'screen active object is not a form i.e. datasheet
MsgBox "Error in " & Me.Name & ".Form_Deactivate: " & Err.Number & " - " & Err.Description
Resume ExitSub
End Sub 'Form_Deactivate
Without setting a Label, is it possible for a VBA Error Handler to resume at the beginning of a With statement?
For example:
Dim rst As DAO.Recordset
Set rst = Nothing
On Error GoTo ErrHand
With rst
.AddNew
!MyValue = 1
.Update
.Bookmark = .LastModified
End With
ErrHand:
If Err.Number <> 0 Then
Call SetRST 'Assume this procedure opens the recordset appropriately
Resume
End If
End Sub()
The code will cause an error on the ".AddNew" line, and then when it goes through the error handler will set the recordset, but then it will Resume on the ".AddNew" line. The problem is that it is still within the "With" statement where CommentRST Is Nothing. Is there a way to tell the error handler to resume at the "With RST" line instead or the ".AddNew" line without creating a label before the "With" statement or checking for a blank recordset first?
I know there are ways around this (as I've just suggested 2 of them), but am curious as to whether this is possible.
Just add a byRef argument to SetRST.
e.g:
Sub SetRST(byref myrs as recordset)
'do stuff
set myrs = ...
By the way, your error handling sample sucks: just add an Exit Sub before ErrHand:,
so you won't need to test if err.number<>0, because you know it will always be.
In your error handling, use:
call SetRST rst
Edit:
I would prefer something like:
If rst Is Nothing Then
Set rst = something
End if
With rst
'continue here
In this case, how would the compiler know to resume at the beginning of the With block (and not at some other point)? It would not, and although it may be logically connected (i.e., it's within the With block) that's still no reason to assume by rule that execution should resume at the start of that block, without explicit reference to resume at that point.
What you're asking essentially assumes the source of the error, and then expects that VBA has this assumption built in, but it would certainly not be applicable for all or even most cases, consider below, assume the rst is open/etc., the actual error raises at the Bookmark property, your error handler doesn't account for that, and so resuming at the beginning of the block will cause infinite failure loop!
On Error GoTo ErrHand
With rst
.AddNew
!MyValue = 1
.Update
.Bookmark = "George Washington"
End With
See the documentation on the Resume statement:
https://msdn.microsoft.com/en-us/library/office/gg251630.aspx
There are your options to Resume
If the error occurred in the same procedure as the error handler, execution resumes with the statement that caused the error. If the error occurred in a called procedure, execution resumes at the statement that last called out of the procedure containing the error-handling routine.
Or to Resume Next:
If the error occurred in the same procedure as the error handler, execution resumes with the statement immediately following the statement that caused the error. If the error occurred in a called procedure, execution resumes with the statement immediately following the statement that last called out of the procedure containing the error-handling routine (or On Error Resume Next statement).
Or to Resume <line>
Execution resumes at line specified in the required line argument. The line argument is a line label or line number and must be in the same procedure as the error handler.
A With block holds an instance of an object, and releases it at End With. If you jumped outside the With block, the object is gone.
So the answer is no, you can't Resume into the middle of a With block. well you actually can, but things get ugly and confusing.
This is a common misuse of the With statement you have here - you're merely using it because you're being lazy (no offense intended), and don't want to type rst. in front of every line that uses that object.
Proper usage would have the With block itself hold the reference, like this:
With GetRecordset 'a function that returns a recordset
.AddNew
!MyValue = 1
.Update
.Bookmark = .LastModified
.Close
End With
..not that I'd recommend working with recordsets like this, but you get the idea ;)
I am writing a VBA function to import data from one table to another in Access. The table I'm importing into has more strict data constraints (i.e. types, size etc.), so I'm expecting a lot of errors.
Rather than sift through every VBA error that comes up, I want my recordset loop to skip the entire current record and make a note of it in a separate table whenever it runs into an error. So every other line I've inserted On Error GoTo RecordError. But for some reason it's not handling every error. My code just breaks and tells me what the error is. I have the "Break on Unhandled Exceptions" option checked already.
Here's a screenshot that should explain it.
Why would it be breaking on the line immediately following an Error handler?
I think you're not understanding how VB(A) error handling works. Follow these principles:
An On Error... statement only applies to the routine (Sub or Function) in which it appears (though it will also catch errors that "bubble up" from routines that are called from within the routine in which you use it).
On Error sets a state. That is, Once you issue an On Error... it remains in force for the rest of the routine, unless superceded by a new On Error....
There are four forms of On Error...:
On Error GoTo <label>: <label> must be defined in the same routine, by writing the label name immediately followed by a colon (:) on a line by itself.
On Error Resume: immediately retries the error-throwing statement. Hardly ever used, since it's potentially infinite.
On Error Resume Next: ignores the error & continues. Sometimes useful at the end of routines for cleanup (for instance, if you want to Close a Recordset that may or may not be open). Alternatively, this form can also be used if you check the Err object immediately after any potentially error-throwing line (if Err.Number is zero (0), the statement succeeded without throwing an error). This is way too much work for most situations.
On Error GoTo 0: turns off error handling.
Given this, it's usual to place the On Error... statement immediately followng the routine's declaration (the Sub or Function statement), though some people put their Dim statements in between. If you want to temporarily change the manner of error handling within a routine, put the "new" one right before the code to which it is to apply, and (if used), the "revert" (reissuing the original), right after.
Even given all that, I have no idea why it would break on the error-throwing line when "Break on Unhandled Errors" is selected, unless you've managed to confuse it so much that it thinks there's no active error handling (and I'd be surprised if it compiled if that were the case).
Note that David Heffernan gave you the essential part of this in his answer, and it was here before mine....
The reason it is not working is because you cannot use On Error Goto ... within an error handler.
see http://www.cpearson.com/excel/errorhandling.htm
you cannot use On Error to skip a few lines, instead on error should go to a error handler which then resume's to the desired next line (in your example you could probably get away with one error handler which contains a resume next which will take you back to the next field).
thanks to Tim Williams on this question: The second of 2 'On Error goto ' statements gets ignored
and BTW ParseInt on a ZIP will destroy zip codes that begin with a 0, zipcodes should probably be treated as text.
You need to place the On Error line before the code whose errors you wish to handle.
What's more you only need to have one On Error line. The error handler then stays active until the subroutine exits or you execute another On Error statement.
Error handling with VBA is a real PITA. I'd propose you to have a look at this answer to the 'MS-Access, VBA and error handling' question, and have it adapted to your own situation. You can easily write down some code that will store all your error messages in a table, building a de facto error reporting system.
Setting the debug mode to 'break on all errors' will make the program execution stop at the line that causes an error even when the error handler has been correctly written. This can be confusing as it appears that error handling is not working.
Nobody has really answered your question.
Say your code is something like this (a skeletal framework):
Public Sub MySub()
On Error GoTo errHandler
Dim rs As DAO.Recordset
Set rs = CurrentDB.OpenRecords([SQL SELECT])
If rs.RecordCount >0 Then
rs.MoveFirst
Do Until rs.EOF
[do whatever that produces the error]
errSkipToNext:
rs.MoveNext
Loop
End If
exitRoutine:
If Not (rs Is Nothing) Then
rs.Close
Set rs = Nothing
Exit Sub
errHandler:
Select Case Err.Number
Case X, Y, Z ' where these are error numbers you want to ignore
Err.Clear
' do whatever it is you need to do in order to record the offending row
Call RecordError(rs!PK, Err.Number) ' PK is a field that identifies the bad record
GoTo errSkipToNext
Case Else
MsgBox Err.Number & ": " & Err.Description, vbExclamation, _
"Error!"
Resume exitRoutine
End Select
End Sub
In this code, you use a SELECT CASE in your error handler to decide which errors you want to ignore. In my code framework above, I listed the error numbers as X, Y, Z, but you'd replace that with the real error numbers you want to ignore, instead.
You don't want to ignore every single error because you might end up ignoring important errors elsewhere in your subroutine. If you don't want to figure out what the limited number of errors you want to ignore happen to be, I would suggest that you set a flag at the beginning of the code block that produces the errors you want to ignore, then use an `If bolErrorInCodeBlockToIgnore Then to decide if you're ignoring all errors or not. Something like this:
Public Sub MySub()
On Error GoTo errHandler
Dim rs As DAO.Recordset
Dim bolErrorInCodeBlockToIgnore As Boolean
Set rs = CurrentDB.OpenRecords([SQL SELECT])
If rs.RecordCount >0 Then
rs.MoveFirst
Do Until rs.EOF
bolErrorInCodeBlockToIgnore = True
[do whatever that produces the error]
errSkipToNext:
rs.MoveNext
Loop
End If
exitRoutine:
If Not (rs Is Nothing) Then
rs.Close
Set rs = Nothing
Exit Sub
errHandler:
If bolErrorInCodeBlockToIgnore Then
Err.Clear
' do whatever it is you need to do in order to record the offending row
Call RecordError(rs!PK, Err.Number) ' PK is a field that identifies the bad record
bolErrorInCodeBlockToIgnore = False
GoTo errSkipToNext
Else
MsgBox Err.Number & ": " & Err.Description, vbExclamation, _
"Error!"
Resume exitRoutine
End If
End Sub
I would much prefer the first, as I'm a firm believer in only ignoring known errors, not any old error that happens. But it might be quite difficult to come up with tests that will produce all the possible errors you want to ignore.
I have seen error handling fail too. Here is one example.
Public Function Have(ByVal item As Variant) As Boolean
'Have = Have data. Simplifies handling nulls and empty strings in validation code
On Error GoTo Procerr
If IsNull(item) Then
Have = False
**ElseIf Len(Trim(item)) = 0 Then 'Faster than Item <> ""**
Have = False
ElseIf item = 0 Then
Have = False
Else
Have = True
End If
exitproc:
Exit Function
Procerr:
'Errors sometimes occur if an unbound control is referenced
Have = False
End Function
The code sometimes fails on the line flagged with **. Here is the error message.
Note that the error handler has failed. In this case, the form that called the code returned had its recordsource set on the fly to an empty recordset, hence the fields on the screen are not visible. The form is a continuous form, so records and fields are not visible when the form is loaded with an empty recordset. The have() function is not directly called by my code, but somehow seems to be triggered by the me.requery method. The have() has been called hundreds of millions of times in my code but this is the only instance that causes it to fail and the error handler is not involked.
To Lance Roberts re original question. utf-8 unicode can sometimes play havoc with ms-access as it seems to be allow data to be confused for instruction codes (my guess). utf-8 can get into your data if data was originally loaded from a text file. utf-8 with a byte order mark (BoM) is particularly nasty. When you run some procedure that works with the data, strange errors can occur and it may look like your file has become corrupt. In other cases, text handling functions give wrong answers, e.g. Mid() will see the BOM and if you specify a starting point will start at the BOM, but Len() ignores the BOM. I am speculating that if you have this issue, then ms-access may not handle errors correctly. I have had similar issues importing data and importing utf-8 as ANSI was the cause. Note that utf-8 and ANSI are identical most of the time for plain English data so your errors may not be on every line. My errors were mostly with time-date fields. Try exporting the data first and then forcing it to be ANSI and remove any BoM and and reimporting it.