DEV Community

Cover image for REST API Fehlerbehandlung Best Practices: Statuscodes, RFC 9457 und Wiederholbare Fehler
Emre Demir
Emre Demir

Posted on Originally published at apidog.com

REST API Fehlerbehandlung Best Practices: Statuscodes, RFC 9457 und Wiederholbare Fehler

API-Fehlerbehandlung in REST-APIs: Ein belastbarer Fehlervertrag

Die Fehlerantworten Ihrer API sind Teil ihres Vertrags. Clients parsen sie, Retry-Logiken verzweigen sich anhand ihrer, und Support-Teams durchsuchen sie um 2 Uhr morgens. Trotzdem entwerfen viele Teams den Erfolgsfall detailliert und überlassen Fehler dem Framework-Standard. Das Ergebnis: drei verschiedene Fehlerstrukturen in einer API, eine 200-Antwort mit "success": false und ein Stack-Trace, der das Datenbankschema öffentlich macht.

Jetzt Apidog ausprobieren

Dieser Leitfaden zeigt, wie Sie Fehlerverträge für REST-Dienste konsistent entwerfen und testen: vom passenden HTTP-Statuscode über RFC-9457-Problem-Details bis zu maschinenlesbaren Fehlercodes, Retry-Semantik, Korrelations-IDs und dem Schutz vertraulicher Informationen. Er ergänzt unsere Analyse dazu, welche HTTP-Statuscodes REST-APIs verwenden sollten.

1. Mit dem Statuscode beginnen

HTTP liefert bereits die erste Ebene der Fehlersemantik. RFC 9110 definiert:

  • 4xx: Der Client hat eine ungültige Anfrage gesendet. Eine Wiederholung derselben Anfrage wird erneut fehlschlagen.
  • 5xx: Der Server ist fehlgeschlagen. Die Anfrage des Clients kann trotzdem korrekt gewesen sein.

Generische Clients, Proxys, Caches und Retry-Bibliotheken treffen ihre Entscheidungen anhand des Statuscodes, ohne den JSON-Body zu lesen. Verwenden Sie daher zuerst den passenden Code. Die vollständige Referenz der HTTP-Statuscodes hilft beim Detaildesign.

Situation Verwenden Sie Nicht Warum
Fehlformatierte Anfrage: defektes JSON, falscher Content-Type, fehlendes Pflichtfeld 400 Bad Request 422 Der Server kann die Anfrage nicht parsen oder verstehen.
Wohlgeformte Anfrage verletzt Domänenregeln: negativer Betrag, nicht unterstützte Währung 422 Unprocessable Content 400 Die Syntax ist korrekt, aber die Werte sind ungültig.
Keine Anmeldeinformationen oder ungültiges/abgelaufenes Token 401 Unauthorized 403 Der Client hat seine Identität nicht nachgewiesen. Senden Sie WWW-Authenticate.
Gültige Anmeldeinformationen, aber unzureichende Berechtigungen 403 Forbidden 401 Die Identität ist bekannt, der Zugriff bleibt verweigert.
Ressource existiert nicht oder ihre Existenz soll verborgen bleiben 404 Not Found 410 Der sichere Standard verhindert auch unbefugtes Sondieren.
Ressource wurde absichtlich und dauerhaft entfernt 410 Gone 404 Clients und Crawler sollen ihre Referenzen löschen.
Konflikt mit dem aktuellen Ressourcenstatus: doppelter Schlüssel, veraltete Version 409 Conflict 400 Die Anfrage ist gültig, kollidiert aber mit dem aktuellen Zustand.
Ratenlimit überschritten 429 Too Many Requests 503 Fügen Sie Retry-After hinzu, damit Clients korrekt zurückweichen.
Unbehandelte Ausnahme im eigenen Code 500 Internal Server Error 502 Der Fehler liegt in Ihrem Server.
Upstream-Dienst liefert eine ungültige Antwort an Ihr Gateway 502 Bad Gateway 500 Der Fehler liegt stromabwärts des Edge.
Server überlastet oder in Wartung 503 Service Unavailable 500 Der Zustand ist definitionsgemäß vorübergehend.
Upstream-Dienst antwortet nicht rechtzeitig 504 Gateway Timeout 500 Der Code unterscheidet eine langsame Abhängigkeit von einem Defekt im eigenen Code.

Zwei Regeln sind besonders wichtig:

  1. 401 und 403 sind eine Sicherheitsgrenze, keine Stilfrage. Ein 403 für nicht authentifizierte Anfragen kann die Existenz einer Ressource verraten.
  2. 429 sollte immer Retry-After enthalten. Ohne ein konkretes Rückzugssignal können Clients Sie in engen Schleifen weiter anfragen. Weitere Details zur Implementierung von API-Ratenbegrenzungen finden Sie in unserem separaten Leitfaden.

2. Ein einheitlicher Fehlerbody mit RFC 9457

Nachdem der Statuscode feststeht, sollte jeder Fehler denselben Medientyp und dasselbe Grundschema verwenden. Der Standard dafür sind RFC 9457 Problem Details, ausgeliefert als:

Content-Type: application/problem+json
Enter fullscreen mode Exit fullscreen mode

Das Format definiert fünf Kernmitglieder:

  • type: URI zur Identifizierung der Fehlerkategorie
  • title: kurze, menschlich lesbare Zusammenfassung
  • status: HTTP-Statuscode, der der Einfachheit halber wiederholt wird
  • detail: Beschreibung dieses konkreten Auftretens
  • instance: URI oder Kennung dieses spezifischen Fehlers

Eigene Informationen kommen in Erweiterungsmitglieder. Eine Validierungsantwort für einen Zahlungs-Endpunkt kann so aussehen:

POST /v1/payments HTTP/1.1
Content-Type: application/json

{ "amount": -1400, "currency": "USD", "source": "card_8xKt2" }
Enter fullscreen mode Exit fullscreen mode
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/validation-error",
  "title": "Request validation failed",
  "status": 422,
  "detail": "One or more fields failed validation.",
  "instance": "/v1/payments/requests/req_9f3c1a7b",
  "code": "PAYMENT_VALIDATION_FAILED",
  "errors": [
    {
      "field": "amount",
      "code": "AMOUNT_NOT_POSITIVE",
      "message": "amount must be a positive integer in minor units"
    }
  ],
  "request_id": "req_9f3c1a7b"
}
Enter fullscreen mode Exit fullscreen mode

Das errors[]-Array ist eine Erweiterung und für Clients besonders nützlich: Ein Frontend kann jeden Fehler direkt dem passenden Formularfeld zuordnen.

Halten Sie Feldpfade stabil. Entscheiden Sie sich beispielsweise für JSON Pointer oder Punktpfade und verwenden Sie anschließend nur dieses Format.

Geben Sie diese Struktur für jeden Fehler zurück – auch für Fehler, die Ihr Framework oder Gateway erzeugt.

Wenn Ihre Handler Problem Details liefern, der Load Balancer bei 502 aber HTML zurückgibt, müssen Clients weiterhin zwei Parser implementieren. Eine ausführlichere Erklärung aller Mitglieder und Registrierungsregeln bietet unser RFC-9457-Erklärer.

3. Maschinenlesbare Codes von Nachrichten trennen

Das Beispiel enthält sowohl code als auch message. Beide Felder bedienen unterschiedliche Zielgruppen.

Maschinenlesbare Codes

Codes wie diese sind Teil des Vertrags:

AMOUNT_NOT_POSITIVE
CURRENCY_UNSUPPORTED
IDEMPOTENCY_KEY_REUSED
Enter fullscreen mode Exit fullscreen mode

Sie müssen stabil, dokumentiert und aufzählbar sein. Clients dürfen niemals Prosa parsen:

// Fragil: eine Textänderung wird zur Breaking Change
if (error.message.includes("positive")) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Verwenden Sie stattdessen den stabilen Code:

if (error.code === "AMOUNT_NOT_POSITIVE") {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Menschliche Nachrichten

Nachrichten dürfen Sie jederzeit verbessern. Sie sollten erklären, was fehlgeschlagen ist und wie der Fehler behoben werden kann:

„Betrag muss eine positive Ganzzahl in kleineren Einheiten sein“

ist hilfreicher als:

„Ungültiger Betrag“

Wenn Sie Nachrichten lokalisieren, lokalisieren Sie nur die Nachricht – der Fehlercode bleibt unverändert. Das ist auch für autonome Clients wichtig: Strukturierte, selbsterklärende Fehler helfen LLM-basierten Clients bei der Wiederherstellung. Mehr dazu: API-Fehlerdesign für KI-Agenten.

4. Diese Informationen gehören niemals in Antworten

Fehlerantworten sind ein bevorzugter Aufklärungskanal für Angreifer. Ihre Fehler-Middleware sollte verhindern, dass Folgendes einen Client erreicht:

  • Stack-Traces, Klassennamen oder Dateipfade
  • Rohes SQL, Abfragefragmente oder ORM-Fehler
  • Interne Hostnamen, IPs, Ports oder Dienstnamen
  • Bibliotheksversionen und Framework-Banner
  • Geheimnisse, Tokens oder Verbindungszeichenfolgen aus Ausnahmetexten
  • Die Information, ob ein Benutzerkonto existiert, etwa bei Login- und Passwort-Reset-Endpunkten

Fangen Sie Ausnahmen an der Systemgrenze ab und protokollieren Sie die vollständigen Details serverseitig mit einer Anfrage-ID. Der Client erhält beispielsweise:

{
  "detail": "An internal error occurred",
  "request_id": "req_51ad0"
}
Enter fullscreen mode Exit fullscreen mode

Ihre Logs enthalten die eigentliche Ausnahme. Der Support kann beide Informationen über request_id miteinander verknüpfen.

5. Fehler als wiederholbar oder terminierend kennzeichnen

Jede Fehlerantwort muss die Frage beantworten: Soll der Client es erneut versuchen?

Die Standardsemantik der Statuscodes ist ein guter Ausgangspunkt:

  • Wiederholbar: 429, 502, 503, 504
  • Vorsichtig wiederholbar: 500
  • In der Regel terminierend: die meisten anderen 4xx-Antworten, insbesondere 401, 403, 404 und 422

Wiederholungen sollten exponentielles Backoff und Jitter verwenden. Berücksichtigen Sie Retry-After, wenn der Header vorhanden ist.

Timeouts sind besonders relevant: Eine Anfrage kann serverseitig erfolgreich gewesen sein, nachdem der Client aufgegeben hat. Bei mutierenden Endpunkten sollten Sie deshalb Idempotenzschlüssel unterstützen, damit eine wiederholte Zahlung nicht doppelt belastet wird.

Die Retry-Semantik lässt sich zusätzlich explizit im Fehlerbody festlegen:

{
  "type": "https://api.example.com/problems/rate-limited",
  "title": "Too many requests",
  "status": 429,
  "code": "RATE_LIMITED",
  "retryable": true,
  "retry_after_seconds": 30
}
Enter fullscreen mode Exit fullscreen mode

Mit retryable können Sie die Standardsemantik gezielt überschreiben, etwa einen bestimmten 500-Untercode als terminierend markieren, wenn ein weiterer Versuch den Zustand beschädigen würde.

6. Korrelations-IDs und Versionierung

Zwei kleine Designentscheidungen sparen später viel Arbeit.

Jede Anfrage erhält eine ID

Akzeptieren Sie einen eingehenden X-Request-Id-Header oder generieren Sie selbst eine ID. Schreiben Sie sie in jede Logzeile und geben Sie sie in jeder Fehlerantwort als request_id zurück.

In verteilten Systemen sollten Sie zusätzlich traceparent nach dem W3C-Trace-Kontext-Standard weiterreichen, damit die Anfrage über mehrere Dienste hinweg verfolgt werden kann.

Fehlerverträge versionieren

Behandeln Sie Ihren Fehlervertrag wie Ihre übrige API:

  • Neue Erweiterungsmitglieder sind in der Regel sicher.
  • Neue Fehlercodes sind in der Regel sicher.
  • Das Umbenennen von errors[].field ist eine Breaking Change.
  • Eine Änderung der Bedeutung eines Codes ist eine Breaking Change.
  • Der Wechsel von einem Ad-hoc-Format zu Problem Details ist eine Breaking Change.

type-URIs bieten eine saubere Evolutionsstrategie: Halten Sie bestehende URIs dauerhaft stabil und führen Sie für neue Semantiken neue URIs ein. Dokumentieren Sie außerdem, dass Clients unbekannte Erweiterungsmitglieder und unbekannte Fehlercodes ignorieren müssen. So können Sie den Vertrag weiterentwickeln, ohne sofort eine v2 zu veröffentlichen.

7. Jeden Fehlerpfad in Apidog testen

Fehlerverträge verrotten, wenn sie nie ausgeführt werden. Der Erfolgsfall läuft in jeder Demo; der 422-Pfad wird erst ausgeführt, wenn ein Kunde ihn auslöst. Machen Sie Fehlerfälle deshalb zu einem festen Bestandteil Ihrer Testsuite.

Serverseitige Testszenarien

Legen Sie pro Endpunkt Szenarien für die relevanten Fehlerfälle an:

  • Fehlende Authentifizierung → 401
  • Unzureichende Rolle → 403
  • Negativer Betrag → 422 mit errors[0].code = AMOUNT_NOT_POSITIVE
  • Burst-Traffic → 429 mit Retry-After

Mit den visuellen Assertions von Apidog können Sie Status, Header und Body-Felder prüfen, ohne für jede Assertion ein Skript zu schreiben. Validieren Sie außerdem den gesamten Body gegen Ihr Problem-Details-JSON-Schema. So schlägt eine Änderung der Fehlerstruktur bereits im CI fehl und nicht erst in der Produktion. Die Muster dafür zeigt der Leitfaden zu API-Assertierungen.

Clientseitige Tests mit Mock-Servern

Frontend- und SDK-Teams müssen 4xx- und 5xx-Antworten testen können, bevor das Backend sie zuverlässig erzeugt. Apidog-Mock-Server liefern die exakten Problem-Details-Bodies aus Ihrer API-Spezifikation.

Damit können Sie unter anderem simulieren:

  • 503 mit Retry-After: 120
  • 409 bei einer doppelten Übermittlung
  • vollständige errors[]-Payloads für Validierungsfehler

Anschließend können Sie prüfen, wie der Client Fehler rendert und Wiederholungen ausführt – ohne manuelle Express-Stubs oder temporäre Änderungen am Backend.

Entwerfen Sie den Fehlervertrag, kodieren Sie ihn als Testszenarien und Mocks und integrieren Sie beides in Ihr CI. Laden Sie Apidog herunter und testen Sie es kostenlos. Der Import einer bestehenden OpenAPI-Spezifikation liefert innerhalb weniger Minuten mockbare Fehlerantworten.

FAQ

Sollte ich 400 oder 422 für Validierungsfehler verwenden?

Verwenden Sie 400, wenn die Anfrage fehlformatiert ist und der Server sie nicht verstehen kann – etwa bei ungültigem JSON, einem falschen Content-Type oder einem fehlenden Pflichtfeld.

Verwenden Sie 422, wenn die Anfrage sauber geparst wird, aber Domänenregeln verletzt – etwa bei einem negativen Zahlungsbetrag oder einer nicht unterstützten Währung.

Wichtig ist vor allem, dass Sie die Entscheidung über alle Endpunkte hinweg konsistent anwenden.

Was ist application/problem+json?

application/problem+json ist der von RFC 9457 definierte Medientyp für Problem Details, das standardisierte JSON-Fehlerformat für HTTP-APIs. Es enthält die Mitglieder type, title, status, detail und instance sowie eigene Erweiterungen, beispielsweise ein errors[]-Array für Feldvalidierungsfehler.

Der registrierte Medientyp ermöglicht es generischen Clients und Middleware, Ihre Fehler ohne individuelle Konfiguration zu erkennen.

Welche HTTP-Fehler sollten Clients automatisch wiederholen?

Wiederholen Sie 429, 502, 503 und 504 mit exponentiellem Backoff und Jitter. Berücksichtigen Sie Retry-After, sofern vorhanden. Behandeln Sie 500 als vorsichtig wiederholbar.

Andere 4xx-Antworten sollten Clients normalerweise nicht wiederholen, da dieselbe Anfrage erneut auf dieselbe Weise fehlschlägt. Verwenden Sie bei mutierenden Endpunkten Idempotenzschlüssel, damit Wiederholungen keine doppelten Zahlungen oder Ressourcen erzeugen.

Wie teste ich API-Fehlerantworten, ohne mein Backend zu beschädigen?

Simulieren Sie die Antworten. Richten Sie Ihren Client auf einen Apidog-Mock-Server, der die exakten 4xx- und 5xx-Bodies aus Ihrer Spezifikation zurückgibt. Testen Sie anschließend Rendering und Retry-Verhalten für jeden Fehlerfall.

Auf der Serverseite senden Testszenarien ungültige Payloads, fehlende Authentifizierung und Burst-Traffic. Prüfen Sie Statuscodes, Header und das Fehlerbody-Schema im CI. So bleibt der Fehlervertrag verlässlich, ohne dass jemand Fehler manuell erzwingen muss.

Top comments (0)