VBA-Fehlerbehandlung — `On Error` richtig einsetzen
Strukturierte Fehlerbehandlung in Access-VBA: `On Error GoTo`, das `Err`-Objekt, Bubble-Pattern und der seltene Sonderfall `Resume Next`.
VBA ist alt, VBA ist eckig, VBA ist in tausenden Access-Anwendungen die Geschäftslogik der Bestandsdaten. Wer dort produktiv Fehler behandelt, kommt um drei Werkzeuge nicht herum: On Error GoTo, das globale Err-Objekt und ein konsequent durchgezogenes Bubble-Pattern. Das vierte Werkzeug — On Error Resume Next — ist ein Spezialwerkzeug, kein Standardansatz.
Das Grundmuster
Jede nicht-triviale Prozedur bekommt einen Fehlerblock am Ende, einen sauberen Exit und einen einzigen On Error GoTo-Aufruf am Anfang:
Public Sub ImportiereCSV(ByVal sPfad As String)
On Error GoTo Fehler
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("tbl_Import", dbOpenDynaset)
' ... eigentliche Logik ...
ExitProc:
On Error Resume Next
If Not rs Is Nothing Then rs.Close
Set rs = Nothing
Set db = Nothing
Exit Sub
Fehler:
Call LogError("ImportiereCSV", Err.Number, Err.Description, sPfad)
Resume ExitProc
End Sub
Drei Dinge sind hier wichtig: Der ExitProc-Block räumt Objekte auf, egal ob ein Fehler auftrat oder nicht. Das On Error Resume Next direkt vor dem Aufräumen verhindert, dass ein schon kaputtes rs beim Close einen zweiten Fehler wirft. Der Fehler-Block protokolliert und springt zurück nach ExitProc — niemals direkt Exit Sub, sonst werden Ressourcen leakt.
Das Err-Objekt
Err ist global und enthält im Fehlerfall alle relevanten Informationen: Err.Number, Err.Description, Err.Source. Wer eigene Fehler wirft, nutzt Err.Raise mit einer Nummer ab vbObjectError + 513 (alles darunter ist Microsoft-Reserve):
Public Function PruefeKundennummer(ByVal sNr As String) As Boolean
Const ERR_FORMAT As Long = vbObjectError + 1001
Const ERR_LEER As Long = vbObjectError + 1002
If Len(sNr) = 0 Then
Err.Raise ERR_LEER, "PruefeKundennummer", _
"Kundennummer darf nicht leer sein."
End If
If Not sNr Like "K[0-9][0-9][0-9][0-9][0-9]" Then
Err.Raise ERR_FORMAT, "PruefeKundennummer", _
"Kundennummer entspricht nicht dem Muster Knnnnn."
End If
PruefeKundennummer = True
End Function
Der Aufrufer fängt diese Fehler dann gezielt ab und entscheidet, ob er weiterläuft, dem Anwender eine Meldung zeigt oder weiter nach oben durchreicht.
Bubble-Pattern: Fehler nach oben durchreichen
In einer Drei-Schichten-Architektur (Formular → Geschäftslogik → Datenzugriff) sollte der Fehler nicht in der untersten Schicht in einem MsgBox enden. Die Datenzugriffsschicht loggt technisch und reicht weiter; die Geschäftslogik entscheidet, was ein Anwender davon sieht; das Formular zeigt eine verständliche Meldung. Der Fehler-Block einer Zwischenschicht sieht dann so aus:
Fehler:
Call LogError("BuchungenSpeichern", Err.Number, Err.Description, "")
Dim lNr As Long, sBeschr As String
lNr = Err.Number
sBeschr = Err.Description
Resume Bubble
Bubble:
On Error GoTo 0
Err.Raise lNr, "BuchungenSpeichern", sBeschr
Die Zwischenvariablen sind nötig, weil Err nach Resume zurückgesetzt wird. Der Wechsel auf On Error GoTo 0 vor dem Err.Raise stellt sicher, dass nicht versehentlich der eigene Fehler-Block erneut anspringt — ein Endlosrekursions-Klassiker.
Wann Resume Next legitim ist
On Error Resume Next hat genau zwei berechtigte Einsatzfälle. Erstens das schon gezeigte Aufräumen am Prozedurende. Zweitens „Try-Probe-Patterns”, in denen ein Fehlschlag erwartet und sofort geprüft wird:
On Error Resume Next
Set ws = Application.CurrentProject.AllForms("frmKunde")
If Err.Number <> 0 Then
Err.Clear
' Formular existiert nicht — alternativer Zweig
End If
On Error GoTo Fehler
Wichtig: direkt nach der Probe wieder zurück auf GoTo Fehler schalten und Err.Clear aufrufen. Wer Resume Next über ganze Prozeduren laufen lässt, baut sich eine Anwendung, in der Fehler stumm verschwinden und Datensätze halb geschrieben werden. Das ist der häufigste Grund, warum Access-Anwendungen „manchmal seltsame Daten” enthalten.
Was sich in zehn Jahren Praxis bewährt hat
Eine zentrale LogError-Prozedur mit Tabelle tbl_ErrorLog (Spalten: Zeitpunkt, Modul, Nummer, Beschreibung, Kontext, Benutzer) ist Pflicht. Sie kostet zwei Stunden Initialaufwand und spart drei Tage Fehlersuche pro Jahr. Pro Modul ein Konstantenblock mit den eigenen Fehlernummern, dokumentiert in einer Code-Kommentartabelle. Und: jede Public-Prozedur ohne On Error GoTo ist ein latenter Bug — auch die kurze.
Saubere Fehlerbehandlung in VBA sieht repetitiv aus, weil sie es ist; genau das macht sie verlässlich.