JavaScript Errors

Error Handling in JavaScript

Fehler passieren. Netzwerke fallen aus, Inputs überraschen einen, APIs ändern sich. Ein gutes Error Handling verhindert, dass aus diesen Fehlern Systemausfälle und verärgerte Nutzer werden.

intermediate14 min readUpdated 15. Sept. 2026
js
// api.js
class HttpError extends Error {
  constructor(status) {
    super(`Request failed with status ${status}`);
    this.name = "HttpError";
    this.status = status;
  }
}

try {
  const res = await fetch("/api/data");
  if (!res.ok) throw new HttpError(res.status);
  const data = await res.json();
} catch (error) {
  if (error instanceof HttpError) report(error.status);
  else console.error(error);
}
Basistyp
Error
Auslösen mit
throw
Behandeln mit
try / catch / finally
Erweitern mit
class extends Error
Async
try/catch + rejected promises
Sicherheitsnetz
Globale Error Handler

Praxis

Try, catch und finally

Ändere die Zahl, um den Fehler auszulösen, und beobachte die Reihenfolge der Meldungen.

Try, catch und finally

Ändere die Zahl, um den Fehler auszulösen, und beobachte die Reihenfolge der Meldungen.

Warum es wichtig ist

Warum Error Handling wichtig ist

Graceful Failure

Ein behandelter Fehler hält die App am Laufen und zeigt dem Nutzer etwas Nützliches an, anstatt eines leeren Bildschirms.

Schneller debuggen

Gute Fehlermeldungen enthalten eine Nachricht, einen Typ und einen Stack Trace, wodurch ein Mysterium zu einem fünfminütigen Fix wird.

Vertrauen aufbauen

Klare Recovery-Strategien und ehrliche Nachrichten lassen eine App solide wirken, selbst wenn das Netzwerk es nicht ist.

Das Gesamtbild

Drei Fragen, die jeder Fehler aufwirft

Ein gutes Error Handling beantwortet jedes Mal dieselben drei Fragen: Was ist schiefgelaufen, wer sollte es wissen und was passiert als Nächstes.

Detection

Was ist schiefgelaufen

Wirf präzise, typisierte Fehler direkt am Ort des Scheiterns, anstatt einen fehlerhaften Zustand im System zu verbreiten.

Recovery

Wer behandelt es

Fange Fehler dort ab, wo du sinnvoll darauf reagieren kannst, und lass alles andere nach oben durchreichen (bubble up).

Communication

Was passiert als Nächstes

Sage dem Nutzer, was er tun kann, logge die Details für Entwickler und halte die App am Leben.

Error Handling auf einen Blick

Die Tools, die du verwenden wirst

Error-Objekte

message, name, stack und cause — die integrierte Struktur eines Fehlers.

throw

Löse eigene Fehler in dem Moment aus, in dem eine Regel verletzt wird.

try / catch

Kapsle riskanten Code ein und behandle den Fehler, ohne dass die App abstürzt.

finally

Cleanup-Code, der unabhängig vom Erfolg oder Misserfolg des Versuchs ausgeführt wird.

Custom Errors

Subklassen von Error, die domänenspezifische Daten transportieren.

Async Errors

Rejected Promises und try/catch rund um await.

Eine kurze Geschichte

Wie JavaScript lernte, richtig zu scheitern

  1. 1995

    Fehler auf Fehlern

    Frühes JavaScript bietet window.onerror als einziges globales Sicherheitsnetz an.

    95
  2. 1999

    try / catch erscheint

    ES3 bringt strukturiertes Exception Handling in die Sprache.

    99
  3. 2015

    Promises und Rejections

    ES6 macht unhandled promise rejections zu einer neuen Klasse von Fehlern, die verwaltet werden müssen.

    15
  4. 2021

    error.cause

    Fehler können nun die zugrunde liegende Ursache kapseln, wodurch der ursprüngliche Kontext erhalten bleibt.

    21
  5. Heute

    Error-first Kultur

    Tooling, Linter und Frameworks setzen alle auf explizites, typisiertes Error Handling.

    Heute

Der vollständige Leitfaden

Error Handling in JavaScript: Alles was Sie wissen müssen

Warum Fehler unvermeidlich sind

Jedes Programm gerät irgendwann in eine Situation, mit der es nicht gerechnet hat: Ein Server gibt einen 500-Fehler zurück, ein Nutzer gibt Buchstaben in ein numerisches Feld ein, eine Datei fehlt oder eine Drittanbieter-API ändert ihr Format. Bei der Fehlerbehandlung geht es nicht darum, jeden Ausfall zu verhindern – sondern darum, zu entscheiden, was passiert, wenn einer auftritt.

Gut behandelte Fehler halten eine Anwendung am Laufen, geben dem Nutzer hilfreiche Informationen und liefern Entwicklern die Details, die sie benötigen. Schlecht behandelte Fehler führen zu leeren Bildschirmen, lautlosem Datenverlust und Bug-Reports, die unmöglich zu reproduzieren sind.

Das Error-Objekt

JavaScript verfügt über einen integrierten Error-Typ, und jeder geworfene Fehler ist in der Regel eine Instanz davon.

// error.js
const error = new Error("Something broke");

error.message; // "Something broke"
error.name;    // "Error"
error.stack;   // the call stack at the point of creation
error.cause;   // an optional underlying error

Es gibt mehrere integrierte Unterklassen für gängige Kategorien: TypeError, ReferenceError, RangeError, SyntaxError, URIError und EvalError. Diese unterscheiden sich hauptsächlich im Namen und in den Situationen, die sie auslösen, was sie nützlich für das Debugging und für die Verzweigung basierend auf der Art des Fehlers macht.

Fehler auslösen

Verwenden Sie throw, um zu signalisieren, dass eine Regel verletzt wurde. Werfen Sie ein Error-Objekt, niemals einen String oder eine Zahl – nur Error-Instanzen enthalten eine Nachricht, einen Namen und einen Stack Trace.

// validate.js
function divide(a, b) {
  if (b === 0) {
    throw new Error("Cannot divide by zero");
  }
  return a / b;
}

Das frühzeitige Auslösen von Fehlern – genau an dem Punkt, an dem eine Annahme nicht mehr zutrifft – verhindert, dass sich ein ungültiger Zustand tiefer in Ihrem Programm ausbreitet. Dass Fehler in der Entwicklung lautstark gemeldet werden, ist ein Feature und kein Ärgernis.

try, catch und finally

try führt riskanten Code aus. Wenn ein Fehler geworfen wird, springt die Steuerung zu catch. finally wird in jedem Fall ausgeführt, was es zum idealen Ort für Cleanup-Aufgaben macht.

// try.js
try {
  const data = JSON.parse(input);
  save(data);
} catch (error) {
  console.error("Invalid data:", error.message);
} finally {
  setLoading(false);
}

Der Parameter catch ist der geworfene Wert. Modernes JavaScript unterstützt zudem optional catch binding, wenn dieser Wert nicht benötigt wird: catch { ... }. Innerhalb eines catch-Blocks können Sie den Fehler untersuchen, ihn loggen, den Zustand wiederherstellen oder den Fehler erneut werfen, falls Sie ihn nicht behandeln können.

// rethrow.js
try {
  await loadProfile();
} catch (error) {
  log(error);
  throw error; // let a higher layer decide
}

Benutzerdefinierte Error-Klassen

Generische Fehler verlieren an Bedeutung, sobald Ihre App wächst. Benutzerdefinierte Klassen ermöglichen es Ihnen, Kontext hinzuzufügen und basierend auf der Art des Fehlers unterschiedlich zu reagieren.

// http-error.js
class HttpError extends Error {
  constructor(status, message) {
    super(message ?? `HTTP ${status}`);
    this.name = "HttpError";
    this.status = status;
  }
}

class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

Nun können Aufrufer präzise reagieren:

// handle.js
try {
  await save(form);
} catch (error) {
  if (error instanceof ValidationError) {
    showFieldError(error.field, error.message);
  } else if (error instanceof HttpError && error.status === 401) {
    redirectToLogin();
  } else {
    showGenericError();
  }
}

Das ist der Unterschied zwischen „etwas ist schiefgelaufen“ und „das E-Mail-Feld ist bereits belegt“.

Fehler mit cause umschließen

Manchmal möchte man Kontext hinzufügen, ohne die ursprüngliche Fehlerursache zu verlieren. Die Option cause bewahrt die Fehlerkette.

// cause.js
try {
  await db.query(sql);
} catch (error) {
  throw new Error("Failed to load orders", { cause: error });
}

Der äußere Fehler enthält den benutzerfreundlichen Kontext, während error.cause die ursprünglichen Details für Logs und das Debugging beibehält.

Fehler in asynchronem Code

Asynchroner Code hat zwei zusätzliche Fehlerpfade. Bei async/await funktioniert try/catch exakt so wie bei synchronem Code.

// async.js
async function load() {
  try {
    const res = await fetch("/api/data");
    if (!res.ok) throw new HttpError(res.status);
    return await res.json();
  } catch (error) {
    console.error("Load failed:", error);
    throw error;
  }
}

Verwendest du kein await, hänge einen Handler an das Promise an:

// promise.js
fetch("/api/data")
  .then((res) => res.json())
  .catch((error) => console.error(error));

Ein abgelehntes Promise ohne Handler wird zu einer unhandled rejection. In Browsern wird eine Warnung protokolliert; in Node.js kann dies den Prozess beenden. Behandle Rejections immer, und wenn es nur dazu dient, sie zu loggen und erneut zu werfen.

Globale Sicherheitsnetze

Selbst bei sorgfältigem Code wird etwas durchrutschen. Globale Handler fangen das auf, was Sie übersehen haben, damit die App den Fehler melden kann, anstatt lautlos abzustürzen.

// global.js
window.addEventListener("error", (event) => {
  reportToServer(event.error);
});

window.addEventListener("unhandledrejection", (event) => {
  reportToServer(event.reason);
});

In Node.js sind die entsprechenden Mechanismen process.on("uncaughtException") und process.on("unhandledRejection"). Betrachten Sie diese als letzte Instanz für das Logging und ein Graceful Shutdown, nicht als Ersatz für die Fehlerbehandlung an der Stelle, an der sie auftreten.

Validieren, bevor geworfen wird

Nicht jedes Problem ist eine Ausnahme. Erwartbare Bedingungen – leere Eingaben, ein fehlendes optionales Feld, eine Suche ohne Ergebnisse – lassen sich besser durch Validierung als durch Exceptions handhaben.

// guards.js
function formatName(user) {
  if (!user?.name) return "Anonymous";
  return user.name.trim();
}

Reservieren Sie Exceptions für wirklich unerwartete oder nicht behebbare Situationen. Wenn sie für den gewöhnlichen Kontrollfluss verwendet werden, wird der Code langsamer und schwerer nachvollziehbar.

Kommunikation mit Nutzern

Technische Fehlermeldungen sind für Entwickler gedacht. Nutzer müssen hingegen wissen, was passiert ist und was sie als Nächstes tun können.

  • Halten Sie Nachrichten kurz und menschlich: „Wir konnten Ihre Änderungen nicht speichern. Bitte prüfen Sie Ihre Verbindung und versuchen Sie es erneut.“
  • Vermeiden Sie Jargon, Stack Traces und rohe Statuscodes in der Benutzeroberfläche.
  • Bieten Sie einen nächsten Schritt an: Erneut versuchen, zurückgehen, den Support kontaktieren oder offline fortfahren.
  • Loggen Sie die vollständigen Details — Nachricht, Stack, Kontext —, damit der Support den Fehler untersuchen kann.

Best Practices

  • Werfen Sie Error-Objekte, niemals Strings.
  • Nutzen Sie catch nur dort, wo Sie den Fehler beheben oder nützlichen Kontext hinzufügen können.
  • Verwenden Sie benutzerdefinierte Error-Klassen für domänenspezifische Fehler.
  • Verpacken Sie Fehler mit cause, anstatt das Original zu verwerfen.
  • Behandeln Sie Promise-Rejections immer.
  • Implementieren Sie globale Handler als letztes Sicherheitsnetz und loggen Sie diese an einen echten Service.
  • Validieren Sie erwartete Bedingungen, anstatt Fehler zu werfen.
  • Trennen Sie Benutzermeldungen von Diagnosen für Entwickler.
  • Schreiben Sie Tests für Ihre Fehlerpfade, nicht nur für den Happy Path.

Häufige Fehler

  • Fehler durch einen leeren catch-Block einfach “verschlucken”.
  • Strings werfen und dadurch den Stack Trace verlieren.
  • Einen Fehler abfangen und einen Standardwert zurückgeben, der einen echten Bug verschleiert.
  • Vergessen, rejected Promises zu behandeln.
  • Rohe Fehlermeldungen an Benutzer anzeigen.
  • Exceptions für den gewöhnlichen Control Flow verwenden.
  • Davon ausgehen, dass try/catch um einen Promise-Executor herum asynchrone Fehler abfängt — tut es nicht.

Wie geht es weiter?

Fehler treten dort auf, wo asynchroner Code und die Fetch API auf die Realität treffen. Kombinieren Sie die hier vorgestellten Muster mit dem DOM, um benutzerfreundliche Zustände darzustellen, und kehren Sie zu den JavaScript-Grundlagen zurück, wann immer Sie ein TypeError daran erinnert, dass undefined keine Funktion ist.

Fehler werfen

Wirf immer Error-Objekte. Sie enthalten einen Stack Trace und eine Nachricht; Strings enthalten beides nicht.

Bevorzugt
if (!user) {
  throw new Error("User not found");
}
Vermeiden
if (!user) {
  throw "User not found";
}

Erwartet vs. außergewöhnlich

Validiere erwartete Bedingungen; reserviere try/catch für Dinge, die du nicht vorhersagen kannst.

Bevorzugt
if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}
Vermeiden
try {
  JSON.parse(input);
} catch {
  // swallowing every error hides bugs
}

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Error Handling zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Error Handling — mit Quizzen und echtem Code, den Sie im Browser ausführen können.