Lektionen/Grundlagen

Fehlerbehandlung mit On Error

Fortgeschritten11 Min. Lesezeit

Solange alles glattläuft, brauchst du keine Fehlerbehandlung. Aber die Datei fehlt, das Feld ist Null, die Division geht durch null, der Benutzer bricht ab — und plötzlich steht dein Code mit einer hässlichen Fehlermeldung mitten im Ablauf. In dieser Lektion baust du Prozeduren, die solche Situationen kontrolliert abfangen, protokollieren und danach sauber weiterlaufen.

Warum überhaupt abfangen?

Ohne Fehlerbehandlung stoppt VBA bei einem Laufzeitfehler die Ausführung und zeigt einen Dialog. Für den Entwickler ist das beim Testen okay. Für den Anwender in einer fertigen Datenbank ist es ein Problem: offene Recordsets bleiben hängen, halbfertige Transaktionen sind unklar, und der Anwender liest eine Meldung, mit der er nichts anfangen kann.

Die Steueranweisung dafür heißt On Error. Sie gibt es in drei Varianten.

On Error GoTo — der Standard

On Error GoTo Label springt bei jedem Fehler ab dieser Zeile zu einer Sprungmarke. Dort kümmerst du dich um das Problem. Das bewährte Muster sieht so aus:

Public Sub KundenExport()
    On Error GoTo Fehler

    Dim db As DAO.Database
    Dim rs As DAO.Recordset
    Set db = CurrentDb
    Set rs = db.OpenRecordset("SELECT * FROM tblKunden", dbOpenSnapshot)

    Do Until rs.EOF
        Debug.Print rs!Nachname
        rs.MoveNext
    Loop

Aufraeumen:
    ' Wird IMMER durchlaufen — im Erfolgs- wie im Fehlerfall
    If Not rs Is Nothing Then
        If rs.State = 1 Then rs.Close   ' 1 = dbOpen / geöffnet
        Set rs = Nothing
    End If
    Set db = Nothing
    Exit Sub

Fehler:
    MsgBox "Fehler " & Err.Number & ": " & Err.Description, _
           vbCritical, "KundenExport"
    Resume Aufraeumen
End Sub

Drei Bausteine machen dieses Muster aus:

  • Exit Sub vor dem Fehler-Label. Ohne diese Zeile würde der normale Ablauf einfach in den Fehlerblock „hineinlaufen", obwohl gar kein Fehler auftrat.
  • Ein Aufräum-Label, das Recordset und Datenbank schließt — egal ob erfolgreich oder nach einem Fehler.
  • Resume Aufraeumen im Fehlerblock, damit auch im Fehlerfall aufgeräumt wird.

Wichtig: Ein DAO.Recordset immer mit rs.Close schließen und auf Nothing setzen. Vergisst du das im Fehlerfall, bleiben Sperren und Speicher hängen — bei Schleifen über viele Datensätze wird das schnell zum Problem.

Die Resume-Varianten

Im Fehlerblock entscheidest du mit Resume, wie es weitergeht:

AnweisungWirkung
ResumeWiederholt die Zeile, die den Fehler ausgelöst hat.
Resume NextMacht mit der nächsten Zeile nach dem Fehler weiter.
Resume LabelSpringt zu einer benannten Marke (z. B. zum Aufräumen).
Exit Sub / Exit FunctionVerlässt die Prozedur direkt.

On Error Resume Next — gezielt, nicht faul

On Error Resume Next ignoriert einen Fehler und macht mit der nächsten Zeile weiter. Das ist kein Freibrief, sondern nur für eng begrenzte Stellen gedacht, an denen du den Fehler direkt danach selbst prüfst:

Public Function TabelleExistiert(ByVal name As String) As Boolean
    Dim def As DAO.TableDef
    On Error Resume Next
    Set def = CurrentDb.TableDefs(name)   ' wirft Fehler, wenn nicht vorhanden
    TabelleExistiert = (Err.Number = 0)
    On Error GoTo 0                        ' Fehlerbehandlung wieder abschalten
End Function

On Error GoTo 0 schaltet die Behandlung wieder aus, damit nachfolgende Fehler nicht stillschweigend verschluckt werden.

Warnung: Ein globales On Error Resume Next über eine ganze Prozedur versteckt echte Fehler und macht die Suche zur Qual. Setz es eng, prüf Err.Number sofort, schalt es gleich wieder ab.

Das Err-Objekt auswerten

Nach einem Fehler stehen die Details im globalen Err-Objekt:

EigenschaftInhalt
Err.NumberDie Fehlernummer (0 = kein Fehler).
Err.DescriptionDer Klartext zur Nummer.
Err.SourceDie Quelle (Projekt- oder Objektname).

Mit Err.Clear setzt du das Objekt zurück. Es wird außerdem automatisch geleert, sobald ein Resume, Exit oder ein neues On Error läuft.

Eigene Fehler auslösen: Err.Raise

Manchmal willst du selbst einen Fehler werfen, etwa wenn eine Vorbedingung verletzt ist. Dafür gibt es Err.Raise. Für eigene Nummern benutzt du den Bereich ab vbObjectError + 512, um Kollisionen mit Access-Fehlern zu vermeiden:

Public Sub RechnungBuchen(ByVal betrag As Currency)
    If betrag <= 0 Then
        Err.Raise vbObjectError + 513, "RechnungBuchen", _
                  "Der Betrag muss größer als 0 sein."
    End If
    ' ... eigentliche Buchung ...
End Sub

Dieser Fehler landet dann in der aufrufenden Prozedur genauso im Err-Objekt wie ein eingebauter Fehler.

Typische Access-Fehlernummern

Ein paar Nummern begegnen dir im Access-Alltag immer wieder:

NummerBedeutung
6Überlauf (Wert passt nicht in den Datentyp).
11Division durch null.
13Typen unverträglich (Type mismatch), oft durch Null.
91Objektvariable nicht gesetzt (Set vergessen).
94Unzulässige Verwendung von Null.
3021Kein aktueller Datensatz (BOF/EOF, Recordset leer).
3061Zu wenige Parameter — meist eine offene Referenz in der Abfrage.
3078Access findet die Tabelle oder Abfrage nicht.
2501Eine DoCmd-Aktion wurde abgebrochen (z. B. OpenForm).
2450Access findet das referenzierte Formular nicht.

Zentrale Fehlerbehandlung und Logging

In größeren Projekten willst du Fehler nicht in jeder Prozedur einzeln vertexten, sondern zentral protokollieren. Eine kleine Hilfsprozedur schreibt jeden Fehler in eine Log-Tabelle tblFehlerLog mit den Feldern Zeitpunkt, Quelle, Nummer, Beschreibung:

Public Sub FehlerLoggen(ByVal quelle As String, _
                        ByVal nummer As Long, _
                        ByVal beschreibung As String)
    Dim rs As DAO.Recordset
    Set rs = CurrentDb.OpenRecordset("tblFehlerLog", dbOpenDynaset, dbAppendOnly)
    rs.AddNew
    rs!Zeitpunkt = Now
    rs!Quelle = quelle
    rs!Nummer = nummer
    rs!Beschreibung = Left$(beschreibung, 255)
    rs.Update
    rs.Close
    Set rs = Nothing
End Sub

Der Fehlerblock jeder Prozedur ruft dann nur noch diese eine Stelle auf:

Fehler:
    FehlerLoggen "KundenExport", Err.Number, Err.Description
    MsgBox "Beim Export ist ein Fehler aufgetreten. " & _
           "Der Vorfall wurde protokolliert.", vbExclamation
    Resume Aufraeumen

So sieht der Anwender eine verständliche Meldung, und du hast im Log alle Details für die spätere Analyse.

Zusammengefasst

  • On Error GoTo Label ist der Standard; kombiniere ihn mit Exit Sub, einem Aufräum-Label und Resume.
  • On Error Resume Next nur eng begrenzt einsetzen und Err.Number sofort prüfen, dann mit On Error GoTo 0 abschalten.
  • Das Err-Objekt liefert Number, Description und Source; mit Err.Raise wirfst du eigene Fehler.
  • Recordset und Datenbank immer im Aufräum-Label schließen, damit im Fehlerfall nichts hängen bleibt.
  • Eine zentrale Log-Prozedur hält die Fehlerblöcke schlank und liefert dir eine auswertbare Historie.
Nächste Lektion
Debuggen im VBA-Editor