Errors
Error-Envelope-Form, voller Error-Code-Katalog, Retry-Semantik und Rate-Limit-spezifisches Handling.
Jede Moonborn-Error-Response teilt dasselbe Envelope:
{
"error": {
"code": "rate_limited",
"message": "Per-minute API rate limit exceeded.",
"details": { "retryAfter": 12 }
}
}code ist ein stabiler, maschinenlesbarer String. message ist
human-readable (sicher, in Logs zu zeigen; zeige es ohne Übersetzung
nicht End-Usern). details trägt code-spezifische Felder, wenn
relevant.
HTTP-Status-Code-Mapping
| Status | Wann |
|---|---|
400 | malformed Request (Parse-Error, fehlendes Pflichtfeld) |
401 | fehlender oder ungültiger Bearer-Token |
403 | authentifiziert aber unauthorized (Scope oder Rolle unzureichend) |
404 | Resource nicht gefunden |
409 | Conflict — typischerweise Idempotency-Key-Collision, Drift-Block oder Persona-State-Machine-Refusal |
422 | Validation-Error (Form gültig, Semantik nicht) |
429 | Rate-Limited |
500 | unerwarteter Server-Error |
502/503/504 | Upstream-LLM-Provider-Problem; retry |
Error-Code-Katalog
| Code | Status | Bedeutung |
|---|---|---|
unauthorized | 401 | Bearer-Token fehlt oder abgelaufen |
forbidden | 403 | Token gültig, unzureichender Scope/Rolle |
not_found | 404 | Resource existiert nicht oder ist Caller nicht sichtbar |
validation_failed | 422 | Request-Body-Form gültig, aber Wertebereich / Enum / Pflichtfeld-Mismatch |
idempotency_conflict | 409 | Selber Idempotency-Key, anderer Payload |
state_conflict | 409 | Resource-State-Machine verweigerte den Übergang (z. B. eine bereits gelöschte Persona archivieren) |
drift_blocked | 409 | Drift-Detection action=block trippte auf dieser Antwort |
audit_blocked | 409 | Audit-Verdict unter Schwelle; Persona im flagged-State zurückgegeben |
moderation_blocked | 422 | Output failte Moderation; Antwort verweigert |
rate_limited | 429 | Per-Tier-Rate-Cap getroffen; details.retryAfter (Sekunden) trägt den Cooldown |
quota_exceeded | 429 | Monatsquota getroffen (z. B. Generation-Quota) |
provider_error | 502 | Upstream-LLM-Provider-Failure; retry mit Backoff |
provider_timeout | 504 | Upstream-LLM-Provider Timeout |
internal_error | 500 | Unhandled Exception; bei uns geloggt |
Retry-Semantik
Die SDKs auto-retryen diese Codes mit Exponential Backoff:
provider_error(5xx)provider_timeout(504)rate_limited(429) — ehrtRetry-After-Header
Diese Codes werden nie auto-retry'd:
validation_failed— fix den Request zuerstidempotency_conflict— ändere den Keydrift_blocked/audit_blocked/moderation_blocked— Caller- Entscheidung
Idempotency-Key
Jeder Write-Request akzeptiert einen Idempotency-Key-Header.
Replays innerhalb von 24 Stunden geben die Original-Response zurück.
SDKs auto-generieren den Key per Default; überschreibe per-Call,
wenn du einen deterministischen Business-Key hast
(Idempotency-Key: order-12345).
Rate-Limit-Header
Jede Response (Erfolg oder Error) trägt:
X-RateLimit-Limit: 3000
X-RateLimit-Remaining: 2987
X-RateLimit-Reset: 1747498200
Nutze X-RateLimit-Remaining für Backpressure in deinem Client.
Siehe Rate limits für Per-Tier-Caps.
Ehrlicher Scope
Wir tracken die Codes oben; wir bringen keine internen Exception-
Typen, Stack-Traces oder Upstream-Provider-Error-Codes an die
Oberfläche. Wenn du einen provider_error debuggst, trägt der Audit-
Log (GET /v1/audit/events) das Upstream-Error-Envelope unter
details.