Zum Hauptinhalt springen

Fehlerbehandlung

REST und GraphQL verwenden unterschiedliche Fehlerformate:

  • REST-Endpoints folgen den RFC 9457 Problem-Details und liefern den passenden Non-2xx-HTTP-Statuscode zurück.
  • GraphQL-Abfrage-, Validierungs- und Laufzeitfehler werden im nativen GraphQL-Antwortformat data + errors zurückgegeben (typischerweise HTTP 200 OK).

REST-Fehlerantwortformat​

REST-Fehlerantworten folgen dieser Struktur:

{
"type": "https://problems.tourfold.com/validation-failed",
"title": "Validation failed",
"status": 422,
"detail": "One or more fields failed validation",
"errors": [
{
"type": "https://problems.tourfold.com/field-size-invalid",
"title": "Field size invalid",
"detail": "The 'title' field exceeds the maximum length of 20 characters",
"pointer": "#/title",
"data": {
"max_length": 20
}
},
{
"type": "https://problems.tourfold.com/invalid-email",
"title": "Invalid email format",
"detail": "The email address format is invalid",
"pointer": "#/driver/email"
}
],
"data": {
"error_count": 2
}
}

Antwortfelder​

Felder auf oberster Ebene​

FeldTypPflichtBeschreibungBeispiel
typestringJaMaschinenlesbare URI des Fehlertyps"https://problems.tourfold.com/validation-failed"
titlestringJaMenschenlesbare Fehlerzusammenfassung"Validation failed"
statusintegerJaHTTP-Statuscode422
detailstringJaAusführliche Fehlerbeschreibung"One or more fields failed validation"
errorsarrayNeinFehlerdetails auf Feldebene(siehe unten)
dataobjectNeinZusätzliche Kontextdaten{"timestamp": "2024-01-15T10:30:00Z"}

Felder im errors-Array​

Jeder Eintrag im errors-Array enthält:

FeldTypPflichtBeschreibungBeispiel
typestringNeinMaschinenlesbarer Fehlertyp auf Feldebene"https://problems.tourfold.com/field-required"
detailstringJaFeldspezifische Fehlerbeschreibung"The 'name' field is required"
pointerstringNeinJSON-Pointer auf das Feld"#/name"
dataobjectNeinFeldspezifische Zusatzdaten{"provided_value": "invalid-email"}

HTTP-Statuscodes​

Der Statuscode sagt Ihnen, wo eine Anfrage fehlgeschlagen ist; type und errors[] sagen Ihnen, was.

StatusBedeutungWann
400 Bad RequestDie Anfrage konnte nicht geparst werden.Fehlerhaftes JSON, ein abgeschnittener Body, eine syntaktisch ungültige Anfrage — der Server hat nie eine wohlgeformte Anfrage erhalten, auf die er reagieren könnte. Problemtyp invalid-input.
402 Payment RequiredDer Workspace ist aufgrund der Abrechnung gesperrt.Beheben Sie die Abrechnung über eine ausgenommene Billing-Recovery-Operation und wiederholen Sie danach die Anfrage. Problemtyp tenant-billing-locked.
422 Unprocessable ContentDie Anfrage war wohlgeformt, wurde aber abgelehnt.Jeder Validierungsfehler bei einer lesbaren Anfrage: ein Feld mit falschem Typ oder falscher Struktur (z. B. 1.5 für ein Integer-Feld, ein String, wo eine Zahl erwartet wird), ein fehlendes Pflichtfeld, ein Wert, der eine Einschränkung verletzt (Länge, Bereich, Format), oder eine Geschäftsregel, die die Anfrage ablehnt. Problemtyp validation-failed, mit errors[], das jedes betroffene Feld über pointer benennt.
401 UnauthorizedDie Anfrage enthielt keine verwendbare Identität.Überhaupt kein Authorization-Header, oder ein Token, das abgelaufen, fehlerhaft oder von einem nicht vertrauenswürdigen Issuer ausgestellt ist. Problemtyp authentication-required; data.reason ist token_missing, token_expired oder token_invalid, und die Antwort enthält einen WWW-Authenticate-Header. Nur token_expired rechtfertigt ein automatisches Erneuern und Wiederholen.
403 ForbiddenDer Aufrufer ist authentifiziert, aber nicht berechtigt.Die Identität ist bekannt, ihr fehlt aber ein erforderlicher Grant, sie ist nicht Eigentümer bzw. Ersteller der Ressource, oder der Plan des Workspace enthält die Funktion nicht. Problemtyp access-denied (oder ein spezifischerer, feature-bezogener Typ); data.reason benennt die Ursache und data.required_grants listet die erfüllenden Grants, wenn die Ablehnung grant-basiert ist. Eine Wiederholung mit derselben Identität hilft nicht.
404 Not FoundDie adressierte Ressource existiert nicht.
405 Method Not AllowedDer Pfad existiert, unterstützt aber nicht die angeforderte HTTP-Methode.Verwenden Sie eine Methode aus dem Allow-Antwortheader. Problemtyp method-not-allowed; data.method enthält die abgelehnte Methode und data.supported die unterstützten Methoden.
406 Not AcceptableDer Endpoint kann keine vom Client akzeptierte Darstellung erzeugen.Senden Sie einen vom Endpoint unterstützten Accept-Header. Problemtyp not-acceptable.
409 ConflictDie Anfrage steht im Konflikt mit dem aktuellen Zustand — z. B. eine Diskrepanz der optimistischen Sperrversion lock_version (resource-conflict).
410 GoneDie zugrunde liegenden Daten einer bekannten Ressource sind dauerhaft nicht verfügbar.Wiederholen Sie die Anfrage nicht unverändert. Der genaue feature-spezifische Typ erklärt, was nicht mehr verfügbar ist.
413 Content Too LargeDie Anfrage überschreitet das für die Operation dokumentierte Payload-Limit.Verkleinern Sie die Anfrage. Problemtyp payload-too-large.
415 Unsupported Media TypeDer Request-Body verwendet einen Medientyp, den der Endpoint nicht akzeptiert.Senden Sie einen in der Operation deklarierten Content-Type, normalerweise application/json. Problemtyp unsupported-media-type; data.content_type enthält den abgelehnten Wert und data.supported die akzeptierten Medientypen.
429 Too Many RequestsDer Client wurde durch Ratenbegrenzung gedrosselt.
503 Service UnavailableEin benötigter Upstream-Service ist vorübergehend nicht verfügbar.Wiederholen Sie die Anfrage entsprechend den Hinweisen der jeweiligen Operation.

Faustregel — 400 vs. 422: Wenn wir Ihre Bytes nicht in eine Anfrage umwandeln konnten, ist es ein 400; wenn wir die Anfrage verstanden haben, sie aber nicht ausführen, ist es ein 422. Ein Feld mit falschem Typ oder ein fehlendes Feld ist immer ein 422 mit einem pointer (kein 400) — nur ein Body, den wir überhaupt nicht parsen können, ist ein 400. Dieselbe Regel gilt für Query- und Pfadparameter: ein Wert, den wir nicht in den erwarteten Typ umwandeln können, ist ein 422, benannt nach dem Parameternamen.

Auth-Fehler-Envelope (401 / 403)​

401-Antworten vom Typ authentication-required und autorisierungsbedingte 403-Antworten verwenden denselben Envelope: neben den üblichen Feldern type / title / status / detail enthält das Problemdokument ein maschinenlesbares data.reason, sodass Clients nie den detail-Text auswerten müssen, um das weitere Vorgehen zu bestimmen. Behandeln Sie data als optional und prüfen Sie die Existenz, bevor Sie darauf zugreifen.

401 — authentication-required. data.reason ist genau einer dieser Werte:

  • token_missing — es wurden keine Anmeldedaten übermittelt. Authentifizieren.
  • token_expired — ein wohlgeformtes, abgelaufenes Token. Access-Token erneuern und einmal wiederholen.
  • token_invalid — fehlerhaftes Token, ungültige Signatur oder nicht vertrauenswürdiger Issuer. Keinen blinden Retry ausführen.

Die Antwort enthält außerdem einen WWW-Authenticate-Header: mit den Parametern nach RFC 6750 (Bearer error="invalid_token", error_description="..."), wenn ein Token übermittelt und abgelehnt wurde, oder als einfaches Bearer, wenn keines gesendet wurde. login-failed ist der einzige weitere Problemtyp, der zu 401 führt; er deckt einen abgelehnten Login mit Anmeldedaten ab, nicht ein unbrauchbares Bearer-Token.

403 — access-denied. data.reason ist genau einer dieser Werte:

  • missing_grant — dem Aufrufer fehlt ein für die Operation erforderlicher Grant.
  • not_owner / not_author / not_member — die Beziehung des Aufrufers zur Ressource passt nicht.
  • plan_restricted — der Plan bzw. die Feature-Flags des Workspace enthalten die Funktion nicht.
  • tenant_scope — die Ressource gehört zu einem anderen Workspace als dem des Aufrufers.
  • resource_locked — der Zustand der Ressource verbietet die Aktion für alle.

data.required_grants listet die durch Doppelpunkte getrennten Grants auf, die die Prüfung erfüllt hätten, z. B. ["users:update"]. Das Feld erscheint nur, wenn die Ablehnung grant-basiert ist und die erforderlichen Grants statisch bekannt sind — Clients müssen es deshalb als optional behandeln. Ein Feature kann anstelle von access-denied auch einen spezifischeren, namensraumbezogenen Typ zurückgeben — zum Beispiel https://problems.tourfold.com/comments/cannot-edit-others — und behält dabei denselben Status und dasselbe data.reason.

Ein typspezifischer 403-Fehler kann ein eigenes Ursachen-Vokabular definieren. Ein deaktivierter Workspace liefert insbesondere https://problems.tourfold.com/tenant-disabled mit data.reason = TENANT_INACTIVE. Prüfen Sie zuerst type, bevor Sie typspezifische data-Felder auswerten; die geschlossene Liste oben gilt für Autorisierungsablehnungen.

Die vollständigen Payloads, die Details zum WWW-Authenticate-Header und die Regeln zur Token-Erneuerung finden Sie unter REST-Authentifizierung.

Fehler in OpenAPI-Operationen​

Die OpenAPI-Beschreibung kombiniert Fehler auf Protokollebene mit Fehlern, die nur bei bestimmten Operationen auftreten:

  • Abgesicherte Operationen dokumentieren 401. Operationen mit einer Antwortdarstellung dokumentieren 406; Operationen mit Request-Body dokumentieren 400 und 415.
  • 405 wird hier statt bei jeder Operation dokumentiert, weil der Status eine HTTP-Methode beschreibt, die für den Pfad gerade keine Operation ist.
  • Kontextabhängige Fehler wie 403, 404, 409, 422, 429 und 503 erscheinen nur bei Operationen, die sie tatsächlich liefern können. Die Beschreibung nennt die jeweilige Bedingung und, sofern stabil, die URI des Problemtyps.
  • 429 und 503 sind keine allgemeinen Standardantworten. Implementiert eine Operation kein Rate-Limit oder hängt sie nicht von einem entsprechend übersetzten, nicht verfügbaren Upstream-Service ab, fehlen diese Antworten bewusst.

Jede Fehlerantwort enthält zusätzlich x-tourfold-problem-types: ein Array mit den stabilen type-URIs auf oberster Ebene, die für diese Operation und diesen Status erwartet werden. Die menschenlesbare Beschreibung nennt dieselben vollständigen URIs. Die Erweiterung beschreibt nur das Problem auf oberster Ebene; Validierungs-Unterprobleme in errors[] verwenden das Standardvokabular der Fehlertypen-Referenz oder von der Operation beschriebene feature-spezifische Typen.

Diese Liste ist ein kuratierter öffentlicher Vertrag und kein vollständiger Dump aller internen Exceptions. Sie enthält erwartete, behandelbare Fehler. Ein unerwarteter Serverfehler kann trotzdem einen unbekannten Problemtyp liefern, auch wenn die Operation keinen generischen 500 bewirbt; Clients benötigen daher immer einen Fallback für unbekannte Typen. Ein neuer Typ ist eine additive Erweiterung. Die Bedeutung oder den Status eines bestehenden Typs zu ändern erfordert eine neue API-Version.

Clients sollten anhand des HTTP-Status und der stabilen type-URI entscheiden, nicht anhand des veränderlichen, menschenlesbaren detail-Texts.

Fehlertyp-System​

Alle URIs der Fehlertypen sind dereferenzierbar. Öffnen Sie eine beliebige Problem-URI im Browser, um ihre Dokumentation anzuzeigen. Die URIs verweisen auf den Katalog der Fehlertypen.

Beispiele:

  • https://problems.tourfold.com/validation-failed – Häufige Validierungsfehler
  • https://problems.tourfold.com/not-found – Ressource nicht gefunden
  • https://problems.tourfold.com/tours/tour-ended – Tour-spezifische Fehler
  • https://problems.tourfold.com/cases/case-already-assigned – Vorgangsspezifische Fehler

Ausprobieren​

Fügen Sie eine Problem-URI in Ihren Browser ein, um sofort die Dokumentation zu sehen:

Vollständiger Katalog​

Alle Fehlertypen und Beschreibungen finden Sie in der Fehlertypen-Referenz.

JSON-Pointer-Syntax​

Wir verwenden die JSON-Pointer-Syntax, um konkrete Felder zu identifizieren:

  • #/name – Feld auf oberster Ebene
  • #/contact/email – Feld in einem verschachtelten Objekt
  • #/drivers/0/email – Element eines Arrays
  • #/settings/notifications/0/channels/1 – Tief verschachteltes Array-Element

Unterstützung für Internationalisierung​

Unsere API-Antworten sind zwar auf Englisch, das strukturierte Fehlerformat erlaubt Anwendungen jedoch, lokalisierte Fehlermeldungen anzuzeigen. Frontends sollten die type-URI als Übersetzungsschlüssel verwenden.

GraphQL-Fehlerantworten​

  • Der HTTP-Status ist üblicherweise 200 OK; Abfrage-, Validierungs- und Laufzeitfehler werden im errors-Array gemeldet.
  • GraphQL-Fehler verwenden nicht den RFC-9457-Problem-Umschlag (type, title, detail, status).
  • Transport-Fehler (zum Beispiel bei der Authentifizierung) können weiterhin Non-200-HTTP-Statuscodes zurückgeben, bevor GraphQL überhaupt ausgeführt wird.

GraphQL-Antwortstruktur​

FeldTypBeschreibung
dataobject, null oder nicht vorhandenAbfrageergebnis. Teilweise, wenn ein nullable Feld gescheitert ist; null, wenn ein non-null Wurzelfeld gescheitert ist; vollständig nicht vorhanden, wenn das Dokument nicht geparst oder validiert werden konnte — dann wurde nichts ausgeführt.
errorsarrayListe der GraphQL-Fehler

Jeder Eintrag in errors enthält typischerweise:

FeldTypBeschreibung
messagestringMenschenlesbare GraphQL-Fehlermeldung
locationsarrayQuellpositionen (line, column) im GraphQL-Dokument
patharray oder nullResolver-Pfad (bei Validierungsfehlern oft null)

GraphQL-Beispiel​

{
"data": null,
"errors": [
{
"message": "Validation error (WrongType@[product]) : argument 'order_by[0].price' with value 'EnumValue{name='invalid_direction'}' is not a valid 'SortDirection' - Literal value not in allowable values for enum 'SortDirection' - 'EnumValue{name='invalid_direction'}'",
"locations": [{ "line": 1, "column": 11 }],
"path": null
},
{
"message": "Validation error (FieldUndefined@[vending_machine/cpu_processor]) : Field 'cpu_processor' in type 'vending_machine' is undefined",
"locations": [{ "line": 5, "column": 9 }],
"path": null
},
{
"message": "Validation error (FieldUndefined@[vending_machine/manufacturer/founding_year]) : Field 'founding_year' in type 'manufacturer' is undefined",
"locations": [{ "line": 8, "column": 13 }],
"path": null
}
]
}

Nächste Schritte​