TypeError: Cannot read properties of undefined (reading 'name') ist die häufigste Zeile in einer Browser-Konsole und eine der am meisten missverstandenen. Fast jeder, der sie sieht, sucht nach dem Ding namens name. Das Ding namens name ist in Ordnung. Der Fehler betrifft das, was es eigentlich enthalten sollte.

Das ist die ganze Schwierigkeit dieser Meldung in einem Satz. Sie nennt die Eigenschaft, die Sie lesen wollten, und sagt nichts über die Variable, die sich als leer herausgestellt hat. Die Lösung liegt daher nie dort, wohin die Meldung zeigt, und die erste Minute jeder Untersuchung geht damit drauf, herauszufinden, wovon die Meldung eigentlich spricht.

Die Meldung lesen

Nehmen Sie den Code user.profile.name. Wenn user existiert, aber kein profile besitzt, dann ist user.profile gleich undefined, und das Lesen von .name auf undefined löst einen Fehler aus. Die Meldung sagt (reading 'name'). Sie sagt nicht, dass profile fehlt, und name ist das einzige Wort im Ausdruck, das nicht das Problem ist.

Verschiedene Browser formulieren das unterschiedlich, was wichtig wird, wenn derselbe Fehler von drei verschiedenen Personen gemeldet wird:

  • Chrome und Edge: Cannot read properties of undefined (reading 'name'). Ältere Versionen meldeten Cannot read property 'name' of undefined, mit derselben Bedeutung.
  • Firefox: TypeError: user.profile is undefined, oder can't access property "name", user.profile is undefined. Firefox benennt den Ausdruck, der leer war, was deutlich hilfreicher ist.
  • Safari: TypeError: undefined is not an object (evaluating 'user.profile.name'). Safari gibt Ihnen den gesamten Ausdruck und überlässt es Ihnen, herauszufinden, welcher Teil davon fehlgeschlagen ist.

Drei Formulierungen, ein Fehler. Ein Team, das Bugs meldet, indem es die Konsolenzeile einfügt, wird drei Tickets eröffnen, und nur das von Firefox sagt tatsächlich, wo man nachsehen muss.

Es gibt eine verwandte Meldung, Cannot read properties of null, und sie ist nicht ganz derselbe Fehler. null erhalten Sie von einer Suche, die gelaufen ist und nichts gefunden hat: document.querySelector('.total') auf einer Seite ohne .total-Element. undefined erhalten Sie von etwas, das nie gesetzt wurde: einer Eigenschaft, die auf dem Objekt nicht existiert, einer Variable, die deklariert, aber nicht zugewiesen wurde, einer Funktion, die nichts zurückgegeben hat. null weist meist auf die Seite hin, undefined meist auf die Daten.

Woher das Undefined kam

Die Meldung ist in jedem Fall dieselbe. Die Ursache stammt aus einer kurzen Liste, und der Eigenschaftsname in Klammern ist ein brauchbarer Hinweis darauf, welche es ist.

Die Daten sind noch nicht angekommen. Die Seite wurde gerendert, bevor die Anfrage, die sie füllt, zurückkam, und der Code las response.items.length, während response noch der Platzhalter war. Das ist der Fall, der bei langsamen Verbindungen auftritt und bei schnellen nicht, auf dem Handy und nicht auf dem Laptop, beim Kunden und nie beim Entwickler. (reading 'length') und (reading 'map') sind die typischen Anzeichen: Etwas hat ein Array erwartet und nichts bekommen.

Die Struktur hat sich geändert. Die API hat früher user.profile zurückgegeben, jetzt liefert sie user.profile nur noch für Konten, die eines ausgefüllt haben, oder sie hat den Schlüssel umbenannt, oder das Feld ist eine Ebene nach oben gewandert. Am Client hat sich nichts geändert, und der Fehler begann an dem Tag, an dem jemand anders ein Deployment gemacht hat. Das ist die Variante, die als „es hat gestern noch funktioniert” ankommt.

Der Schlüssel ist anders geschrieben. item.userId im Code, item.user_id in der Antwort. Nichts warnt Sie davor: JavaScript liest eine nicht existierende Eigenschaft als undefined und macht einfach weiter, und der Fehler zeigt sich erst einen Schritt später, wenn etwas versucht, durch sie hindurchzulesen.

Es ist eine leere Liste. results[0].title, wenn results gleich [] ist. Vollkommen gültiger Code, bis zur ersten Suche, die nichts findet. Testdaten sind selten leer, Produktionsdaten oft.

Etwas hat nichts zurückgegeben. Eine Funktion mit einem Codepfad, der das return vergisst, eine async-Funktion, deren Aufrufer das await vergessen und statt eines Werts ein Promise erhalten hat, ein .find(), das keine Übereinstimmung gefunden hat. All das erzeugt undefined und reicht es an die nächste Zeile weiter.

Der Code ist minifiziert. In der Produktion lautet die Meldung Cannot read properties of undefined (reading 'a'), weil die Eigenschaft durch den Build umbenannt wurde. Die Zeilennummer zeigt auf eine einzige riesige Zeile. Ohne Source Map sagt die Meldung überhaupt nichts mehr aus, und übrig bleibt nur die Abfolge von Anfragen und Klicks, die dazu geführt haben.

Wie es von der anderen Seite aussieht

Von alldem sieht die Person, die darauf gestoßen ist, nichts. Was sie sieht, ist ein Bereich der Seite, der nie gefüllt wurde, ein Button, der nichts tut, ein Formular, das sich nicht absenden lässt. Nichts auf der Seite sagt „ein Fehler ist aufgetreten”, weil der Fehler innerhalb eines Skripts passiert ist und das Skript einfach angehalten hat. Die Konsolenzeile existiert, in einem Panel, das diese Person nie geöffnet hat.

Also steht im Bericht „die Bestellseite ist leer”. Und der Entwickler reproduziert das mit einer schnellen Verbindung, einem vollständigen Konto und einer nicht leeren Liste, und die Seite ist nicht leer.

Schwach
Bestellseite ist bei einem Kunden leer, lässt sich nicht reproduzieren.
Besser
Konsole: TypeError: Cannot read properties of undefined (reading 'items'), orders.js line 214. Die Anfrage an /api/orders direkt davor lieferte 200 mit leerem Body zurück. Kunde hat noch keine Bestellungen.

Die zweite Version ist eine Lösung, die nur noch umgesetzt werden muss. Alles darin stammt von der Seite im Moment des Fehlers: die Konsolenzeile, die Anfrage davor und die Antwort, die die Daten hätte enthalten sollen. Das ist der Beleg, den dieser Fehler braucht, und genau der Beleg, den jemand, der auf eine leere Seite blickt, nicht selbst liefern kann. Unser Leitfaden zum Schreiben eines Bugreports beschreibt, wonach man fragen sollte; kurz gesagt sind bei diesem Fehler die Konsole und das Netzwerkprotokoll der Bericht, und die Beschreibung ist nur die Bildunterschrift.

Session Replay

Kostenlose Chrome-Erweiterung. Ein Klick auf die Seite, die nicht richtig funktioniert, erfasst den Screenshot, die Konsole und das Netzwerkprotokoll und gibt Ihnen einen Link, den Sie in das Ticket einfügen können.

Erweiterung holen

Die Ursache in den Entwicklertools finden

Wenn Sie die Seite vor sich haben und der Fehler sich reproduzieren lässt, klären drei Handgriffe die Sache.

Bei Exceptions anhalten. Im Sources-Panel von Chrome hält das Pause-Symbol mit „Pause on uncaught exceptions” das Skript auf der Zeile an, die den Fehler ausgelöst hat, wobei jede Variable noch im Gültigkeitsbereich vorhanden ist. Fahren Sie mit der Maus über den Ausdruck, und Sie sehen, welcher Teil von user.profile.name undefined ist, was Ihnen die Meldung nie gesagt hätte.

Die Anfrage davor ansehen. Wechseln Sie zum Network-Tab und suchen Sie die Antwort, die das Objekt hätte füllen sollen. In neun von zehn Fällen liegt die Antwort dort: ein leerer Body, ein 200 mit einer Fehlermeldung darin, ein Schlüssel mit einem anderen Namen, oder eine Anfrage, die nie abgeschickt wurde, weil sie auf etwas anderes gewartet hat. Der Artikel zu failed to fetch behandelt den Fall, in dem die Anfrage selbst gescheitert ist; dieser Artikel behandelt den Fall, in dem sie erfolgreich war und die falsche Struktur zurückgegeben hat.

Die Reihenfolge der Ereignisse prüfen. Wenn der Fehler nur manchmal auftritt, ist es fast immer die erste der oben genannten Ursachen: ein Lesezugriff, der ausgeführt wird, bevor die Daten da sind. Drosseln Sie das Netzwerk im Network-Tab auf „Slow 3G” und laden Sie die Seite neu. Tritt der Fehler zuverlässig auf, handelt es sich um eine Race Condition, und die Lösung besteht darin, auf die Daten zu warten, statt den Lesezugriff abzusichern.

Die Lösung, die keine ist

user?.profile?.name lässt den Fehler verschwinden. Es lässt das Profil nicht erscheinen. Optional Chaining verwandelt einen Absturz in ein stilles undefined, das dann als leere Zeichenkette, fehlende Zeile oder Button ohne Beschriftung in die Seite einfließt, und der Bug ist jetzt für Sie unsichtbar und für den Kunden weiterhin sichtbar.

Es ist das richtige Werkzeug, wenn das Fehlen berechtigt ist: ein Konto, das wirklich noch kein Profil hat. Es ist das falsche Werkzeug, wenn das Fehlen der eigentliche Bug ist. Die Frage, die Sie sich vor dem Hinzufügen eines ?. stellen sollten, lautet, ob der Wert jemals fehlen dürfte. Wenn nicht, wollte Ihnen der Absturz etwas sagen, und ein Guard ist eine Art, nicht hinzuhören.

Was Sie daraus mitnehmen sollten

Die Meldung nennt die Eigenschaft, nach der Sie gefragt haben, und verbirgt das, was leer war. Lesen Sie sie als „der Schritt vor diesem hat nichts hervorgebracht”, und schauen Sie sich diesen Schritt an: die Anfrage, den Rückgabewert, den Schlüsselnamen, die Liste, die leer war. Die Lösung liegt immer vor der Zeilennummer, jedes Mal.

Und wenn der Bericht vom Bildschirm einer anderen Person kommt, sind die Konsolenzeile und das Netzwerkprotokoll kein Kontext zum Bug. Bei diesem Fehler sind sie der Bug.