Eine Prozedur, die monatelang unauffällig ihren Dienst getan hat, bricht plötzlich mit einer kryptischen Meldung ab – und der Anwender steht vor einem Dialog, mit dem er nichts anfangen kann. Der Grund ist fast immer derselbe: Es gibt keine oder nur eine halbherzige Fehlerbehandlung. Dabei ist “On Error” eines der mächtigsten Werkzeuge in VBA, wenn man weiß, wie man es einsetzt. Wir schauen uns die Muster an, die aus einer fragilen Lösung eine robuste machen: wie wir Fehler nicht nur abfangen, sondern strukturiert behandeln, Ressourcen zuverlässig freigeben, Fehler zwischen den Schichten einer Anwendung weiterreichen und sie in einer Tabelle protokollieren. Die Beispiele findest Du im Modul “mdlFehlerbehandlung” der Beispieldatenbank. Wie das im Einzelnen gelingt, zeigt der vorliegende Beitrag.
Was passiert ohne Fehlerbehandlung?
Schauen wir uns zuerst an, was VBA von sich aus tut, wenn ein Laufzeitfehler auftritt.
Die Prozedur DateiOhneSchutz aus folgendem Listing 1 öffnet eine Textdatei, die es gar nicht gibt:
Public Sub DateiOhneSchutz() Dim intFile As Integer intFile = FreeFile Open "C:\NichtVorhanden.txt" _ For Input As #intFile Close #intFile End Sub
Existiert die Datei nicht, bricht VBA mit dem Laufzeitfehler 53 ab und zeigt den Dialog aus Bild 1 – mit einer Meldung, die dem Anwender nichts sagt, und mit den Schaltflächen Beenden und Debuggen, die in einer ausgelieferten Anwendung beide nichts zu suchen haben.

Bild 1: Der Standarddialog von VBA beim Laufzeitfehler 53 – so etwas darf der Anwender nie zu sehen bekommen
Klicken wir auf Debuggen, sehen wir die farbig hinterlegte fehlerhafte Anweisung (siehe Bild 2).

Bild 2: Anschließend wird die fehlerhafte Zeile farbig hinterlegt.
Zwei Dinge sind hier schiefgelaufen: Der Anwender bekommt keine verständliche Meldung, und die Prozedur bricht mitten im Ablauf ab. In anderen Prozeduren können dadurch bereits geöffnete Dateien, Recordsets oder andere Ressourcen unaufgeräumt zurückbleiben.
Die drei Formen von On Error
VBA kennt genau drei Varianten der On Error-Anweisung, und sie bestimmen, was bei einem Laufzeitfehler geschieht:
- On Error GoTo Marke springt zur angegebenen Sprungmarke. Das ist die Standardform für eine strukturierte Fehlerbehandlung.
- On Error Resume Next ignoriert den Fehler und macht mit der nächsten Anweisung weiter. Das ist nützlich, wenn wir den Fehler unmittelbar danach selbst prüfen wollen.
- On Error GoTo 0 schaltet jede aktive Fehlerbehandlung in der aktuellen Prozedur ab. Ein Fehler führt danach wieder zum Standarddialog.
Ein Punkt, der später noch wichtig wird: On Error gilt immer nur innerhalb der Prozedur, in der es steht. Eine globale Fehlerbehandlung gibt es in VBA nicht – jede Prozedur muss sich selbst schützen.
Das Standard-Pattern: On Error GoTo
Das bewährteste Muster sieht so aus: Am Anfang der Prozedur leiten wir auftretende Fehler auf eine Sprungmarke um, am Ende springen wir über den Fehlerblock hinweg. Bauen wir unser Beispiel entsprechend um – das Ergebnis heißt DateiMitSchutz und steht in folgendem Listing:
Public Sub DateiMitSchutz() On Error GoTo ErrHandler Dim intFile As Integer intFile = FreeFile Open "C:\NichtVorhanden.txt" For Input As #intFile ' ... Datei verarbeiten ... Close #intFile ExitHere: Exit Sub ErrHandler: MsgBox "Fehler " & Err.Number & ": " _ & Err.Description, vbCritical, "DateiMitSchutz" Resume ExitHere End Sub
Der Ablauf im Einzelnen: On Error GoTo ErrHandler aktiviert die Fehlerbehandlung – ab hier springt VBA bei einem Laufzeitfehler zur Marke ErrHandler.
Läuft alles glatt, erreichen wir ExitHere und verlassen die Prozedur mit Exit Sub. Tritt dagegen ein Fehler auf, landen wir im ErrHandler, zeigen eine Meldung an und springen mit Resume ExitHere zur Austrittsmarke.
Warum eigentlich Resume ExitHere und nicht einfach GoTo ExitHere? Der Unterschied sieht nach Geschmackssache aus, ist aber entscheidend: Resume meldet VBA, dass der Fehler behandelt wurde. Erst danach kann überhaupt wieder ein Fehler abgefangen werden. Mit GoTo bleibt die Prozedur dagegen im Fehlerzustand.
Den Fehlerzustand sichtbar machen
Das ist eine Behauptung – schauen wir sie uns an, statt sie zu glauben. Die Prozedur GoToStattResume in macht den Unterschied im Direktbereich sichtbar:
Public Sub GoToStattResume() On Error GoTo ErrHandler Err.Raise 53 ExitHere: Debug.Print "In ExitHere: Err.Number = " & Err.Number Exit Sub ErrHandler: Debug.Print "Im Handler: Err.Number = " & Err.Number GoTo ExitHere End Sub
Rufe die Prozedur im Direktbereich mit GoToStattResume auf. Die Ausgabe zeigt in beiden Zeilen die 53 – der Fehler ist in ExitHere also immer noch aktiv (siehe Bild 3).

Bild 3: Der Direktbereich zeigt es schwarz auf weiß: Nach GoTo steht Err.Number immer noch auf 53
Tauschst Du das GoTo ExitHere gegen Resume ExitHere, steht in der zweiten Zeile eine 0: Der Fehler gilt als erledigt.
Und genau daran hängt die Praxisfolge – ein weiterer Fehler in der Aufräumlogik würde in der GoTo-Variante nicht mehr abgefangen, sondern führte direkt zum Standarddialog.
Das Err-Objekt im Detail
Das Err-Objekt ist immer verfügbar und beschreibt den zuletzt aufgetretenen Fehler. Wichtig sind drei Eigenschaften:
- Number ist die Fehlernummer, wobei 0 für “kein Fehler” steht. Für eigene Fehler nutzen wir Nummern ab vbObjectError + 512 – dazu kommen wir gleich.
- Description liefert die Fehlerbeschreibung im Klartext.
- Source nennt die Quelle: standardmäßig den Namen des VBA-Projekts, bei externen Bibliotheken deren Namen, etwa DAO.Database oder ADODB.Connection.
Dazu kommen zwei Methoden. Clear setzt Number auf 0 und leert die übrigen Eigenschaften des Err-Objekts. Beim Verlassen einer Prozedur und durch eine Resume-Anweisung wird der Fehlerzustand ebenfalls zurückgesetzt. Wenn wir mehrere Operationen unter On Error Resume Next prüfen, sollten wir Err.Clear dagegen ausdrücklich zwischen den Operationen aufrufen.
Und Raise löst einen Laufzeitfehler aus – damit erzeugen wir gleich gezielt eigene Fehler.
Resume – drei Varianten
Aus dem Fehlerblock heraus haben wir drei Möglichkeiten, den Programmfluss fortzusetzen. Resume führt die fehlerauslösende Zeile erneut aus – sinnvoll nur dann, wenn wir die Ursache im Fehlerblock tatsächlich beseitigt haben.
Resume Next macht mit der Zeile danach weiter, wenn der Fehler tolerierbar ist. Und Resume Marke springt zu einer bestimmten Marke, was die häufigste Variante ist: Genau so kommen wir geordnet zur Austrittsmarke.
Wie Resume gezielt eingesetzt werden kann, zeigt das folgende Listing. Die Prozedur OrdnerAnlegen legt einen Ordner an, dessen übergeordneter Ordner womöglich noch fehlt – in dem Fall legen wir diesen an und versuchen es erneut:
Public Sub OrdnerAnlegen(strPfad As String) On Error GoTo ErrHandler MkDir strPfad ExitHere: Exit Sub ErrHandler: If Err.Number = 76 Then 'Pfad nicht gefunden: 'übergeordneten Ordner anlegen MkDir Left(strPfad, InStrRev(strPfad, "\") - 1) Resume Else MsgBox "Fehler " & Err.Number & ": " _ & Err.Description Resume ExitHere End If End Sub
Aufgerufen wird das etwa so:
OrdnerAnlegen "C:\Temp\Export\Kunden"
Zwei Stolpersteine stecken hier drin: Erstens legt die Korrektur nur eine Ebene an – fehlen zwei, scheitert schon das MkDir im Fehlerblock. Ein weiterer Fehler innerhalb eines bereits aktiven Fehlerhandlers kann von demselben Handler nicht erneut behandelt werden. Gibt es keinen übergeordneten aktiven Handler, erscheint der VBA-Standarddialog. Zweitens droht eine Endlosschleife, wenn die Korrektur das Problem gar nicht löst: Resume wiederholt dann immer wieder dieselbe Zeile. Wie wir das mit einem Zähler entschärfen, sehen wir weiter unten.
On Error Resume Next – gezielt und sparsam
On Error Resume Next hat einen schlechten Ruf, und der ist meistens verdient: Wer es als Allzweckwaffe einsetzt, verschluckt Fehler stillschweigend und macht die Anwendung unberechenbar. Es gibt aber Situationen, in denen es genau das richtige Werkzeug ist – nämlich dann, wenn wir den Fehlerstatus einer einzelnen Operation bewusst abfragen wollen.
Ein typischer Fall: Wir wollen wissen, ob ein Eintrag in einer Collection existiert. Eine Exists-Methode gibt es dort nicht, also nutzen wir den Zugriffsfehler als Prüfmittel. Das erledigt die Funktion ExistiertInCollection. Sie erwartet die zu prüfende Collection und den Schlüssel und liefert True oder False zurück:
Public Function ExistiertInCollection(col As Collection, _ strKey As String) As Boolean Dim varDummy As Variant On Error Resume Next varDummy = col(strKey) ExistiertInCollection = (Err.Number = 0) On Error GoTo 0 End Function
Das Muster dahinter ist immer dasselbe: On Error Resume Next einschalten, die kritische Anweisung ausführen, Err.Number prüfen und sofort danach mit On Error GoTo 0 zurücksetzen – oder, wenn die Prozedur eine eigene Fehlerbehandlung hat, mit On Error GoTo ErrHandler wieder auf die reguläre Marke umleiten. Ausprobieren lässt sich das wie folgt:
Dim col As New Collection col.Add "Wert", "Schluessel" Debug.Print ExistiertInCollection(col, "Schluessel") 'Liefert True Debug.Print ExistiertInCollection(col, "Fehlt") 'Liefert False
Entscheidend ist die Disziplin: On Error Resume Next sollte nie länger als zwei oder drei Zeilen aktiv sein. Alles andere ist ein Wartungsrisiko.
Aufräumen bei Fehlern: das Exit-Pattern
In vielen Prozeduren müssen wir am Ende Ressourcen freigeben – Recordsets schließen, Objektvariablen auf Nothing setzen, Dateien schließen. Diese Arbeit darf der Fehlerblock nicht überspringen.
Deshalb bekommt die Prozedur eine gemeinsame Austrittsmarke, die sowohl der reguläre Ablauf als auch der Fehlerblock anspringt. Wie das aussieht, zeigt die Prozedur DatenExportieren (siehe Listing 1). Sie liest die Tabelle tblKunden aus und schreibt die Namen in eine Textdatei.
Public Sub DatenExportieren(strPfad As String) Dim db As DAO.Database Dim rst As DAO.Recordset Dim intFile As Integer Dim bolDateiOffen As Boolean On Error GoTo ErrHandler Set db = CurrentDb Set rst = db.OpenRecordset("SELECT KundenName FROM tblKunden", dbOpenSnapshot) intFile = FreeFile Open strPfad For Output As #intFile bolDateiOffen = True Do Until rst.EOF Print #intFile, rst!KundenName rst.MoveNext Loop ExitHere: If bolDateiOffen Then Close #intFile bolDateiOffen = False End If If Not rst Is Nothing Then rst.Close Set rst = Nothing End If Set db = Nothing Exit Sub ErrHandler: MsgBox "Fehler in DatenExportieren:" & vbCrLf _ & Err.Number & " - " & Err.Description, _ vbCritical Resume ExitHere End Sub
Listing 1: Beispiel für das Exit-Pattern
Der Aufruf sieht so aus:
DatenExportieren "C:\Temp\Kunden.txt"
Beachte die Prüfung If Not rst Is Nothing: Der Fehler kann auch schon vor dem OpenRecordset auftreten – dann wäre rst noch Nothing, und ausgerechnet das Aufräumen würde einen neuen Fehler auslösen. Ob die Datei tatsächlich geöffnet wurde, halten wir in der Variablen bolDateiOffen fest. Eine von FreeFile gelieferte Dateinummer allein beweist noch nicht, dass die anschließende Open-Anweisung erfolgreich war.
Fehler weiterreichen mit Err.Raise
Nicht jeder Fehler gehört dort behandelt, wo er auftritt. In einer sauber aufgebauten Anwendung gibt es Schichten: Datenzugriff, Geschäftslogik und Benutzeroberfläche. Eine Datenzugriffsfunktion soll dem Aufrufer melden, dass etwas schiefgelaufen ist – aber keine MsgBox anzeigen. Dafür ist Err.Raise da.
Die Funktion KundeNachID liest zu einer Kundennummer den Namen aus tblKunden. Findet sie keinen Datensatz, löst sie selbst einen Fehler aus; tritt ein anderer Fehler auf, räumt sie auf und reicht ihn nach oben weiter (siehe Listing 2).
Public Function KundeNachID(lngID As Long) As String On Error GoTo ErrHandler Dim db As DAO.Database Dim rst As DAO.Recordset Dim lngNummer As Long Dim strQuelle As String Dim strText As String Set db = CurrentDb Set rst = db.OpenRecordset("SELECT Firma FROM tblKunden WHERE KundeID = " & lngID, dbOpenSnapshot) If rst.EOF Then Err.Raise vbObjectError + 1001, "KundeNachID", "Kein Kunde mit der ID " & lngID & " gefunden." End If KundeNachID = rst!Firma ExitHere: If Not rst Is Nothing Then rst.Close Set rst = Nothing End If Set db = Nothing Exit Function ErrHandler: ' Fehlerdaten sichern, bevor wir aufräumen lngNummer = Err.Number strQuelle = Err.Source strText = Err.Description If Not rst Is Nothing Then rst.Close Set rst = Nothing End If Set db = Nothing Err.Raise lngNummer, strQuelle, strText End Function
Listing 2: Fehler weiterreichen mit Err.Raise
Hier geschehen zwei verschiedene Dinge. Findet die Abfrage keinen Kunden, erzeugen wir mit Err.Raise einen eigenen Fehler.
Unser exklusives Angebot für Dich!
(Gilt für den Abschluss eines Jahres-Abonnements.)
Hier geht’s weiter →Die ersten 4 Wochen kostenlos testen – voller Zugriff auf alle Artikel, vollständigen Code und Beispieldatenbanken. Kein Risiko: Wenn es nicht passt, kündigst Du einfach innerhalb der ersten vier Wochen.
Oder hast Du eine konkrete Frage zu Deiner eigenen Access-Anwendung?
Vielleicht stellt Deine Anwendung Dich vor eine Herausforderung, zu der Du bisher keine Lösung findest. Schlechte Performance, kein ausreichender Zugriffsschutz, Du bist unsicher über Dein Datenmodell oder Dein Code liefert unerklärliche Fehler?
In unserem kostenlosen Access-Audit schaut sich André Minhorst persönlich gemeinsam mit Dir Deine Lösung per Zoom an – und zeigt Dir, wo Datenmodell, VBA-Code, Ergonomie und Sicherheit Optimierungspotenzial bieten.
Jetzt kostenloses Access-Audit anfordern →