Fehler behandeln
Kurzantwort: Jeder Fehler kommt als JSON mit error, message,
messageKey, code und layer. Bei Validierungsfehlern kommt zusätzlich
violations mit allen betroffenen Feldern auf einmal — die lassen sich
direkt am Formular anzeigen. Wiederholen lohnt sich nur bei 401, 409 und 503.
Das Format
Abschnitt betitelt „Das Format“{ "error": "ApiValidationException", "message": "validation-failed", "messageKey": "CDMS_VALIDATION_FAILED", "code": "422", "layer": "API", "violations": [ { "field": "name", "rule": "cannot-be-null" }, { "field": "items.0.quantity", "rule": "min-number" } ]}| Feld | wofür Sie es brauchen |
|---|---|
code | der Statuscode, auch im Körper |
messageKey | stabiler Schlüssel — darauf sollten Ihre Übersetzungen zeigen |
message | technische Meldung, oft mit Details nach ` |
error | Ausnahmeklasse, gut fürs Log |
layer | wo es passierte: api, system, database, hook |
violations | nur bei Feldvalidierung |
Zeigen Sie
messagenicht ungefiltert an. Sie ist für Entwickler geschrieben. Bauen Sie Ihre Texte aufmessageKeyundviolationsauf.
Was welcher Code bedeutet
Abschnitt betitelt „Was welcher Code bedeutet“| Code | Bedeutung | Ihre Reaktion |
|---|---|---|
| 400 | Request ist inhaltlich falsch: response fehlt, ungültiger Filter, ungültige UUID | Fehler im Client beheben — nie wiederholen |
| 401 | Token fehlt, abgelaufen oder ungültig | Token erneuern, einmal wiederholen |
| 403 | Rolle fehlt, Mandantenwechsel nicht erlaubt | Zugriff verwehrt — nicht wiederholen |
| 404 | existiert nicht oder ist für Sie unsichtbar | wie „nicht vorhanden” behandeln |
| 409 | Konflikt | kurz warten, wiederholen |
| 413 | Upload-Problem | Dateien und Namen prüfen |
| 422 | Feldregeln verletzt | violations ans Formular |
| 500 | Serverfehler, Fehlkonfiguration, unbekanntes Feld | melden, nicht wiederholen |
| 503 | Dienst vorübergehend nicht verfügbar | mit wachsender Wartezeit wiederholen |
404 statt 403 bei fremden Daten ist Absicht: Der Server verrät nicht, dass ein Objekt existiert, das Sie nicht sehen dürfen. Ein 404 heißt für Sie also „nicht verfügbar”, nicht zwingend „gibt es nicht”.
Validierung ans Formular bringen
Abschnitt betitelt „Validierung ans Formular bringen“type Violation = { field: string; rule: string };
const rules: Record<string, string> = { 'cannot-be-null': 'Pflichtfeld', 'cannot-be-empty': 'Darf nicht leer sein', 'too-long': 'Zu lang', 'pattern-mismatch': 'Ungültiges Format', 'min-number': 'Wert zu klein', 'max-number': 'Wert zu groß',};
function toFormErrors(violations: Violation[]) { return Object.fromEntries( violations.map((v) => [v.field, rules[v.rule] ?? 'Ungültiger Wert']), );}Der Feldpfad enthält die Verschachtelung — items.0.quantity meint das erste
Element der Liste items. Damit markieren Sie auch in dynamischen Formularen
die richtige Zeile.
Alle Verstöße kommen in einer Antwort. Sie müssen nicht Feld für Feld absenden, um alle Fehler zu finden.
Häufige Meldungen und was dahintersteckt
Abschnitt betitelt „Häufige Meldungen und was dahintersteckt“messageKey bzw. message | Ursache |
|---|---|
response (400) | die response-Liste fehlt |
missing-id | PATCH ohne id im data |
missing-object|<id>|<klasse> | verknüpftes Objekt existiert nicht oder ist unsichtbar |
missing-type-for-abstract-field|<feld> | @type fehlt bei einem abstrakten Ziel |
recursive-create-not-allowed|<feld> | über diese Beziehung darf nicht angelegt werden |
missing-permission|<rolle> | Ihnen fehlt diese Rolle |
missing-attribute-on-profile|<attribut> | Ihr Profil trägt ein nötiges Attribut nicht |
object-already-exists|use-update | Singleton existiert schon |
no-data-exists|use-create | Singleton existiert noch nicht |
duplicate-filenames|… | zwei gleichnamige Dateien im Upload |
CDMS_TENANT_NOT_SERVED | der Mandant ist nicht aktiv |
CDMS_TENANT_SWITCH_NOT_AUTHORIZED | Mandantenwechsel nicht erlaubt |
Die Angaben nach | sind Parameter — Feldname, ID, Rollenname. Für eigene
Meldungen lässt sich der Schlüssel am | abschneiden.
Eine brauchbare Fehlerbehandlung
Abschnitt betitelt „Eine brauchbare Fehlerbehandlung“async function call(path: string, body: unknown, retry = true): Promise<unknown> { const res = await fetch(`${base}${path}`, { method: 'POST', headers: { Authorization: `Bearer ${await token()}`, 'Content-Type': 'application/json' }, body: JSON.stringify(body), });
if (res.ok) { const data = await res.json(); // Teilerfolg bei LENIENT: 200, aber ohne Daten if (data?.messageKey === 'CDMS_CREATE_SUCCEEDED_READ_FAILED') { return { partial: true, id: data.id }; } return data; }
const err = await res.json().catch(() => ({}));
if (res.status === 401 && retry) { await refreshToken(); return call(path, body, false); // genau einmal } if (res.status === 422) throw new ValidationError(err.violations ?? []); if (res.status === 404) return null; throw new ApiError(err.messageKey ?? `HTTP ${res.status}`, res.status, err);}Drei Dinge, die dabei zählen:
- Beim 401 nur einmal wiederholen — sonst dreht sich der Client im Kreis, wenn das Problem nicht am Token liegt.
- Den Fehlerkörper immer lesen, auch wenn er leer sein könnte. Er enthält die einzige brauchbare Information.
- Den Teilerfolg abfangen, wenn Sie
LENIENTverwenden — siehe Transaktionen.
Was der Fehler nicht sagt
Abschnitt betitelt „Was der Fehler nicht sagt“Bei einem 500 steht in der Antwort bewusst nur eine grobe Meldung; die
Einzelheiten liegen im Serverlog. Wenn Sie einen Fehler melden, helfen:
messageKey, code, layer, der Zeitpunkt und der aufgerufene Pfad.
Übersicht
Abschnitt betitelt „Übersicht“Die vollständige Liste aller Statuscodes und Ausnahmen steht unter Fehler und Statuscodes.