JavaScript-Fehler im Browser debuggen, statt weiter zu raten
Jede Runde aus console.log und Neuladen beantwortet genau eine Frage von vorhin. Ein bedingter Haltepunkt an derselben Stelle beantwortet sie einmal und vollständig.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Kurz gesagt
Debuggen ist kein Suchen, sondern ein Ausschlussverfahren. Du machst den Fehler reproduzierbar, findest im Call Stack den ersten Frame aus deinem eigenen Code und liest dort den Zustand, statt ihn zu vermuten. Haltepunkte, bedingte Haltepunkte und funktionierende Source Maps ersparen dir dabei die Runden aus Ausgabe ergänzen, neu laden und wieder von vorn anfangen.
Stand dieser Seite: 04.10.2026
01 Worum es geht
Die Konsolenausgabe beantwortet immer nur die Frage von vorhin
Der übliche Ablauf: Es gibt einen Fehler, du setzt eine Ausgabe an die verdächtige Stelle, lädst neu, siehst einen Wert, verstehst ihn nicht, setzt die nächste Ausgabe. Jede Runde kostet eine Minute und liefert genau einen Wert aus genau einem Durchlauf. Bei einem Fehler, der nur beim 417. Eintrag auftritt, ist das keine Suche mehr, sondern eine Zufallsstichprobe.
Der zweite Zeitfresser ist das Springen zwischen Vermutungen. Ohne festes Vorgehen wird die Stelle geprüft, die am leichtesten zu prüfen ist, und nicht die, an der der Wert zum ersten Mal falsch war. Bei Fehlern, die über mehrere Schichten wandern, führt das dazu, dass am Symptom repariert wird: Ein Standardwert deckt das undefined zu, und der eigentliche Fehler wandert eine Ebene tiefer.
Am teuersten sind die Fälle, die nur bei anderen auftreten. Ohne Source Maps im Build zeigt der Stack aus dem Betrieb auf Zeile 1 einer minimierten Datei und auf Funktionsnamen wie t und Ae. Ohne diese Zuordnung ist ein gemeldeter Fehler kaum mehr als der Hinweis, dass irgendwo etwas nicht stimmt, und die Suche beginnt bei null.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
02 Schritt für Schritt
Das Vorgehen in sieben Schritten
Die Reihenfolge ist wichtiger als jedes einzelne Werkzeug. Wer sie einhält, kommt auch bei Fehlern voran, die selten auftreten oder sich auf dem eigenen Rechner nicht zeigen.
- 1
Das Fehlerbild festhalten
Notier die vollständige Meldung mit Call Stack, die Schritte davor, Browser und Version, die Datenlage und die Frage, ob es nur im Betrieb auftritt. Vermeide dabei jede Ursachenvermutung, sie lenkt die Suche in eine Richtung, für die es noch keinen Grund gibt.
Ergebnis: Ein einziger Satz beschreibt Auslöser, Erwartung und Beobachtung, ohne schon eine Deutung zu enthalten.
- 2
Reproduzieren und einkochen
Löse den Fehler bewusst aus, dann entferne alles, was ihn nicht braucht: andere Komponenten, Erweiterungen im Browser, halbe Datensätze. Wenn er erst nach einem bestimmten Stand auftrat, engt git bisect den Zeitraum in wenigen Schritten auf einen einzelnen Stand ein.
Ergebnis: Eine feste Abfolge löst den Fehler zuverlässig aus, notfalls in einer leeren Seite mit zwanzig Zeilen.
- 3
Den ersten eigenen Frame im Stack finden
Prüf zuerst, ob die Source Maps greifen, also ob im Sources-Bereich dein Quelltext steht und nicht die minimierte Version. Setz danach Bibliotheken über die Ignore-Liste stumm, damit der Stack nicht mit fremden Frames beginnt.
Ergebnis: Du kennst eine Datei und eine Zeile aus deinem Code statt nur eines Frames aus dem Framework.
- 4
Anhalten statt ausgeben
Setz den Haltepunkt an diese Zeile und gib ihm eine Bedingung, die nur im Fehlerfall zutrifft. Wenn du den Ort noch nicht kennst, hilft das Anhalten bei Ausnahmen, wahlweise auch bei abgefangenen, oder ein Haltepunkt auf eine Netzwerkanfrage, auf eine DOM-Änderung oder auf einen bestimmten Ereignistyp.
Ergebnis: Der Debugger hält im Fehlerfall an und nicht in den harmlosen Durchläufen davor.
- 5
Den Zustand vergleichen, nicht deuten
Lies im Bereich Scope die tatsächlichen Werte, nicht die erwarteten. Dann klick dich im Call Stack nach oben durch die aufrufenden Frames und prüf denselben Wert dort. Für wiederkehrende Ausdrücke sind Live Expressions praktisch, weil sie ohne erneutes Anhalten aktuell bleiben.
Ergebnis: Die Abweichung ist benannt: Hier ist der Wert bereits falsch, im Frame darüber war er noch richtig.
- 6
Netzwerk und Zeitpunkt prüfen
Sieh im Network-Bereich nach, was tatsächlich über die Leitung kam, und zwar unter Response und nicht unter Preview, denn Preview zeigt eine aufbereitete Ansicht. Prüf Status, Header und Reihenfolge. Mit Drosselung fallen Fehler auf, die auf einer schnellen Verbindung nie auftreten, etwa zwei Anfragen, deren Antworten in umgekehrter Reihenfolge eintreffen.
Ergebnis: Es ist klar, ob die falschen Daten schon vom Server kamen oder erst im Code entstanden sind.
- 7
Die Ursache absichern
Schreib zuerst den Test, der den Fehler nachstellt, und erst danach die Korrektur. Wenn der Test ohne die Änderung nicht fehlschlägt, hast du eine andere Stelle getroffen als gedacht. Notier im Änderungsverlauf die Ursache und nicht die Auswirkung.
Ergebnis: Ein Test wird ohne die Korrektur rot, und ein Eintrag benennt die Ursache.
03 Was du mitnimmst
Was du danach in derselben Zeit erreichst
Der Unterschied zwischen einer halben Stunde und zwei Tagen liegt selten am Werkzeug, sondern an der Reihenfolge. Erst reproduzierbar machen, dann eingrenzen, dann den Zustand lesen, und erst zum Schluss ändern.
Sechs Schritte vom Symptom zur belegten Ursache
- 01 Halte fest, was passiert, ohne dabei schon eine Ursache zu nennen.
- 02 Koch den Fall ein, bis er in wenigen Zeilen zuverlässig auftritt.
- 03 Such im Stack den ersten Frame, der aus deinem eigenen Code stammt.
- 04 Halte dort an und lies den Zustand, statt ihn aus Ausgaben zu erraten.
- 05 Geh im Call Stack zurück, bis der Wert das letzte Mal richtig war.
- 06 Sichere die Ursache mit einem Test ab, der ohne die Korrektur fehlschlägt.
Den Fehler zuerst reproduzierbar machen
Solange du ihn nicht auslösen kannst, kannst du auch nicht prüfen, ob du ihn behoben hast. Feste Eingabedaten, ein bekannter Ausgangszustand und eine notierte Schrittfolge sind Voraussetzung, nicht Fleißarbeit.
Bedingte Haltepunkte statt Ausgaben
Ein Haltepunkt mit der Bedingung index === 417 hält genau im Fehlerfall an und ignoriert die 416 harmlosen Durchläufe davor. Der Code bleibt dabei unverändert, du musst also nichts wieder entfernen.
Den ersten eigenen Frame finden
Setz Framework- und Bibliotheksdateien auf die Ignore-Liste, dann zeigt der Call Stack oben deine eigene Datei. Der Fehler entsteht fast nie in der Bibliothek, sondern in dem, was du ihr übergeben hast.
Rückwärts durch den Call Stack gehen
Wenn ein Wert an der Fehlerstelle falsch ist, klick dich im Call Stack nach oben und prüf ihn in jedem Frame. Die Ursache liegt an der Grenze, an der er das letzte Mal richtig war.
Source Maps auch für den Betrieb bereitstellen
Ohne Zuordnung ist ein Stack aus dem Betrieb wertlos. Mit versteckten Source Maps, die nur an die Fehlerüberwachung gehen, bekommst du lesbare Stacks, ohne den Quelltext auszuliefern.
Jede gefundene Ursache absichern
Ein Test, der ohne die Korrektur fehlschlägt, ist der einzige Beleg dafür, dass du die Ursache und nicht nur ein Symptom getroffen hast. Er verhindert außerdem, dass derselbe Fehler beim nächsten Umbau zurückkommt.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Was der Call Stack wirklich sagt
Die Meldung selbst grenzt schon stark ein, wenn man sie genau liest. x is not defined ist ein ReferenceError: Der Name existiert in keinem erreichbaren Gültigkeitsbereich, also fehlt ein Import, ein Tippfehler steckt drin, oder die Datei wird im falschen Modulformat gelesen. Cannot read properties of undefined (reading 'name') heißt dagegen, dass das Objekt links vom Punkt undefined ist, nicht die Eigenschaft. Und x is not a function heißt, dass die Eigenschaft existiert, aber nichts Aufrufbares enthält, ein klassisches Zeichen für einen falschen Standardimport aus einem CommonJS-Paket.
Der oberste Frame im Stack ist die Stelle, an der es geknallt hat, nicht unbedingt die Stelle mit dem Fehler. Interessant ist der Übergang: der letzte Frame, in dem der Wert noch richtig war. Deshalb ist die Ignore-Liste so nützlich: Sobald die Frames aus fremden Bibliotheken ausgeblendet sind, beginnt der Stack mit deinem Code, und beim schrittweisen Ausführen springt der Debugger nicht mehr in fremde Dateien.
Bei asynchronem Code zeigen die Entwicklungswerkzeuge zusätzlich die Frames vor dem await, gekennzeichnet als asynchroner Teil. Fehlt der Stack ganz und in der Konsole steht nur Uncaught (in promise), gibt es kein await und kein catch an dieser Stelle. Beim Weiterreichen von Fehlern lohnt sich außerdem die Option cause: new Error('Preis nicht ladbar', { cause: fehler }) behält den ursprünglichen Fehler samt seinem Stack.
Haltepunkte, die viele nie benutzt haben
Ein Rechtsklick auf die Zeilennummer bietet mehr als den einfachen Haltepunkt. Ein bedingter Haltepunkt hält nur an, wenn ein Ausdruck wahr ist, etwa bei einer bestimmten Kennung. Ein Logpoint gibt einen Ausdruck aus, ohne anzuhalten und ohne dass du eine Zeile im Code änderst, was die Frage nach vergessenen Ausgaben vollständig erledigt.
Für Fehler ohne bekannte Stelle gibt es Haltepunkte ohne Zeile. Anhalten bei Ausnahmen erwischt den Moment des Wurfs, und die Zusatzoption für abgefangene Ausnahmen findet die Fälle, die ein zu breites catch verschluckt. DOM-Haltepunkte halten an, wenn ein Element verändert oder entfernt wird, was die Frage beantwortet, welcher Code eine Klasse wieder wegnimmt. Ereignis-Haltepunkte greifen bei jedem Listener eines Typs, und ein Haltepunkt auf eine Anfrage mit einem Teil der URL erwischt den Aufrufer eines fetch, ohne dass du ihn vorher kennst.
Zwei Kleinigkeiten sparen viel Zeit. In der Konsole hält debug(funktionsName) beim nächsten Aufruf dieser Funktion an, ohne dass du ihre Datei suchen musst. Und eine debugger-Anweisung im Code ist für schwer erreichbare Stellen legitim, gehört aber nie in einen Branch, der gemergt wird. Die Regel no-debugger im Linter verhindert genau das.
Source Maps, und warum sie im Betrieb oft fehlen
Eine Source Map ordnet jede Position im minimierten Bundle einer Position im Quelltext zu. Der Browser findet sie über einen Kommentar am Dateiende, und ohne sie zeigt jeder Stack auf eine einzige lange Zeile. Im Entwicklungsserver ist das eingeschaltet, im Production Build oft nicht, weil niemand den Quelltext ausliefern will.
Der Ausweg heißt versteckte Source Maps: Der Build erzeugt die Dateien, lässt aber den Verweis am Ende des Bundles weg und lädt sie stattdessen zur Fehlerüberwachung hoch. Dort werden die eingehenden Stacks zugeordnet, während im Browser nichts Zusätzliches liegt. In den Entwicklungswerkzeugen lässt sich eine Map außerdem von Hand nachreichen, über Add source map im Kontextmenü der Datei.
Eine falsche Zuordnung ist schlimmer als gar keine. Wenn nach einem Umbau der Build-Kette die Zeilen um ein paar Stellen verschoben sind, zeigt der Debugger auf eine Anweisung, die mit dem Fehler nichts zu tun hat, und du suchst an einer erfundenen Stelle. Ein kurzer Test gehört deshalb zur Build-Kette: einen Fehler bewusst auslösen und prüfen, ob der Stack auf die richtige Zeile zeigt.
Fehler, die nur bei anderen auftreten
Die häufigsten Unterschiede lassen sich der Reihe nach abarbeiten. Erweiterungen im Browser verändern Seiten, deshalb ist ein privates Fenster der schnellste Ausschlusstest. Ein alter Stand im Cache trifft auf eine neue Schnittstelle, was sich an unpassenden Feldern in den Antworten zeigt. Sprache und Zeitzone unterscheiden sich, und das trifft vor allem Datumsverarbeitung: Ein Datum ohne Zeitzone wird je nach Umgebung anders gelesen, und die Anzeige weicht um einen Tag ab.
Danach kommen die Unterschiede in den Daten. Sonderzeichen, sehr lange Werte, leere Listen und Datensätze aus der Zeit vor einer Umstellung erzeugen Zustände, die in Testdaten nicht vorkommen. Wenn du an den echten Datensatz herankommst, ist der Fehler meist in Minuten reproduziert, und die vorherige Suche im Code wird überflüssig.
Bleibt der Fehler unsichtbar, hilft nur Aufzeichnung aus dem Feld. Eine Fehlerüberwachung mit hochgeladenen Source Maps liefert lesbare Stacks samt Browser, Version und den letzten Schritten davor. Wichtig ist dabei, dass die Meldungen eine eigene Kennung je Vorfall bekommen, sonst lassen sich Bericht und Aufzeichnung später nicht zusammenbringen.
Wann Ausgaben trotzdem das bessere Mittel sind
Es gibt Fälle, in denen ein Haltepunkt die Sache verfälscht. Alles, was mit Zeit zu tun hat, verändert sich, sobald der Debugger anhält: Animationen, Timer, Race Conditions zwischen zwei Anfragen. Auch bei sehr häufigen Aufrufen ist Anhalten unbrauchbar, weil du hundertmal fortsetzen müsstest. Dann sind Ausgaben richtig, am besten als Logpoint statt als Codeänderung.
Für die Darstellung lohnt sich mehr als console.log. console.table zeigt eine Liste von Objekten als Tabelle mit Spalten, console.group fasst zusammengehörige Ausgaben zusammen, console.count zählt Durchläufe ohne eigene Variable, und console.trace gibt an Ort und Stelle den Call Stack aus, was die Frage nach dem Aufrufer sofort beantwortet.
Was Ausgaben nicht ersetzen: den Blick auf den vollständigen Zustand. Eine Ausgabe zeigt genau die Werte, die du dir vorher ausgesucht hast, und Objekte werden in der Konsole erst beim Aufklappen ausgelesen, sodass du unter Umständen einen späteren Stand siehst als den zum Zeitpunkt der Ausgabe. Wer das weiß, gibt bei veränderlichen Objekten lieber eine Kopie aus.
Dazu passende Kurse
Mit einem festen Vorgehen kannst du einen Fehler systematisch einkreisen, statt dich von Symptom zu Symptom zu hangeln, und genau das üben die JavaScript-Kurse bei cmt an vorbereiteten Fällen.
Wenn du Fehler nicht nur finden, sondern ihr Wiederauftreten verhindern willst, führt der Weg über Kurse rund um systematisches Testen.
Wie sicher bist du beim Thema wirklich?
Lesen fühlt sich schnell nach Können an. Ein kurzer Test zeigt dir, was davon schon sitzt und wo sich ein Kurs lohnt. Kostenlos, ohne Anmeldung, mit einer Erklärung zu jeder Antwort.
04 Konkrete Kurse
Wo du genau das übst
- JavaScript Aufbaukurs
- JavaScript - Webanwendungen testen mit Jasmine, Jest, Karma und Puppeteer
- JavaScript Grundkurs
Weitere Kurse aus diesem Bereich
Preise, Orte und Buchung für alle Kurse des Bereichs findest du im Katalog: Alle Schulungen zur Webentwicklung mit Terminen.
Stimmen aus den Kursen
Was Teilnehmende über die Kurse in diesem Bereich sagen
Sehr guter Kurs, den ich nur wärmstens weiterempfehlen kann. Sehr breite Themenaufstellung, die alles abdeckt.
Die Schulung entsprach Meinen Vorstelllungen. Dozent war gut organisiert und strukturiert. Redegeschwindigkeit und Ausdruck war super.
Klare Struktur der Themenblöcke, flexible Gestaltung je nach Kenntnisstand und ein roter Faden, dem man gut folgen kann.
05 Fragen
Häufige Fragen
Deine Frage ist nicht dabei? Stell sie uns direkt, wir antworten dir persönlich.
Wie finde ich den Auslöser, wenn der Stack nur Framework-Dateien zeigt?
Der Fehler tritt nur im Production Build auf, nicht im Entwicklungsserver. Woran liegt das?
Wie kann ich im Betrieb prüfen, ob eine Änderung hilft, ohne neu auszuliefern?
Lohnt sich die debugger-Anweisung im Code?
Deine Ansprechpartner
Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.
Yves Hoppe
Weiterbildung & Beratung
Hilft dir, aus dem Webentwicklung-Programm den passenden Kurs für deinen Stand zu finden.
Norbert Jansen
Beratung & Inhouse
Plant mit dir Inhouse-Trainings, die auf eure Abläufe und euren Datenbestand zugeschnitten sind.
Fehler, die du nicht mehr durch Ausprobieren findest
In den JavaScript-Kursen bei cmt arbeitest du mit vorbereiteten Fehlern und gehst dabei vom Symptom über den Haltepunkt bis zur Ursache, die du belegen kannst.