Wenn etwas schiefgeht
Fehler nachschlagen
#Fehler einfügen
Lesen Sie Fehler in dieser Reihenfolge: HTTP-Status, dann error.type, dann error.code. Der Status ist immer aussagekräftig; type sagt Ihnen, wer den Aufruf abgelehnt hat; code ist nur bei Fehlern stabil, die das Gateway selbst erzeugt.
#Wie Sie verzweigen
- 1
Nach dem Status verzweigen
402 heißt aufladen, 403 heißt Schlüsselberechtigungen, 429 heißt warten, 5xx heißt erneut versuchen. Diese Ebene lügt nie und ändert sich nie.
- 2
error.type prüfen
xiaoxiang_error stammt von uns. Alles andere — openai_error, claude_error, gemini_error — ist der eigene Body des Anbieters, unverändert weitergeleitet.
- 3
Dann error.code lesen
Verzweigen Sie nur bei xiaoxiang_error nach code. Upstream-Codes gehören dem Anbieter und können sich ohne Ankündigung ändern.
Nicht jeder Fehler hat einen JSON-Body
Fehlerstrukturen
Fünf Formen erreichen Ihren Client. Behandeln Sie alle fünf — die letzten drei sind diejenigen, die Integrationen meist übersehen.
Gateway-Fehler
Das Gateway selbst hat den Aufruf abgelehnt — Authentifizierung, Kontingent, Routing, Anfrageformat.
{
"error": {
"message": "insufficient account balance, please top up to continue (request id: 20260910...)",
"type": "xiaoxiang_error",
"code": "insufficient_user_quota"
}
}Upstream-Fehler, durchgereicht
Der Anbieter hat mit einem eigenen Fehler geantwortet. Der Body wird unverändert weitergeleitet, type und code stammen also vom Anbieter.
{
"error": {
"message": "This model's maximum context length is 128000 tokens.",
"type": "invalid_request_error",
"code": "context_length_exceeded"
}
}Ablehnung durch Vorprüfung
Idempotenz und einige weitere Vorprüfungen antworten, bevor das Relay-Envelope erzeugt wird. Flache Struktur, code in Großbuchstaben.
{
"code": "IDEMPOTENCY_IN_PROGRESS",
"error": "a request with this Idempotency-Key is still processing"
}Ablehnung eines asynchronen Tasks
Jeder Task-Endpunkt — Video, Bild-Task, Musik, 3D — meldet Fehler in seiner eigenen flachen Struktur. Hier gibt es kein verschachteltes error-Objekt, ein für /v1/chat/completions geschriebener Parser liest also null.
{
"code": "task_not_exist",
"message": "task does not exist",
"data": null
}Nur Status, leerer Body
Ablehnungen durch das Ratenlimit und zu große Anfrage-Bodys brechen mit einem reinen Statuscode ab.
HTTP/1.1 429 Too Many Requests
Retry-After: 20
Content-Length: 0Antwort-Header
Protokollieren Sie die Anfrage-ID bei jeder Antwort, nicht nur bei Fehlern. Anhand dieser Kennung verfolgt der Support einen Aufruf nach, und sie macht den Unterschied zwischen einer Antwort in fünf Minuten und einem Tag Rätselraten.
| Header | Gesendet, wenn | Was Sie damit tun |
|---|---|---|
| X-Oneapi-Request-Id | Jede Antwort, ob Erfolg oder Fehler. | Protokollieren Sie sie neben Ihrer eigenen Trace-ID. Der Support bearbeitet Tickets anhand dieses Werts, und dieselbe ID wird an Fehlermeldungen des Gateways angehängt. |
| Retry-After | Bei 429 sowie bei 409, solange ein Idempotency-Key noch verarbeitet wird. | Warten Sie mindestens so viele Sekunden. Dies ist der einzige Backoff-Hinweis, den das Gateway sendet. |
| X-Idempotent-Replayed | Bei einem wiederholten Idempotency-Key wird eine gespeicherte Antwort erneut ausgeliefert. | true bedeutet: Es wurde nichts neu ausgeführt und nichts erneut berechnet — behandeln Sie den Body als das ursprüngliche Ergebnis. |
| X-Idempotency-Sealed | Die ursprüngliche Antwort kann nicht sicher erneut ausgeliefert werden (zum Beispiel ein Stream oder ein zu großer Body). | Versuchen Sie es mit diesem Schlüssel nicht erneut. Gleichen Sie stattdessen über den Task-Endpunkt oder Ihre eigenen Aufzeichnungen ab. |
const response = await fetch(url, init);
const requestId = response.headers.get("X-Oneapi-Request-Id");
if (!response.ok) {
// Bodies are absent on 429 and 413 — never assume JSON here.
const detail = await response.text();
logger.error({ status: response.status, requestId, detail });
throw new GatewayError(response.status, requestId, detail);
}#Fehlercode-Tabelle
Fehler, die das Gateway selbst erzeugt, immer mit type "xiaoxiang_error". Zwei Zeilen haben beide den Status 429 und bedeuten Gegensätzliches — lesen Sie den code, bevor Sie Backoff anwenden.
| HTTP | Code | Bedeutung | Was tun | Wiederholen? |
|---|---|---|---|---|
| 401 | keine | Fehlendes, fehlerhaftes, gelöschtes oder deaktiviertes Bearer-Token. Diese Ebene setzt keinen code — werten Sie den Status aus. | Prüfen Sie den Authorization-Header und den Schlüsselstatus unter Konsole → API-Schlüssel. | nein |
| 401 | signature_required | Für den Schlüssel ist Anfragesignierung aktiviert, und der Anfrage fehlt X-Timestamp, X-Nonce oder X-Signature. | Signieren Sie jede Anfrage mit diesem Schlüssel (Authentifizierung & Schlüssel → Anfragesignierung), oder deaktivieren Sie die Signierung für den Schlüssel. | nein |
| 401 | signature_expired | X-Timestamp ist kein 10-stelliger Unix-Sekundenwert oder weicht mehr als 5 Minuten von der Serverzeit ab. Die Antwort enthält X-Server-Timestamp. | Korrigieren Sie die Client-Uhr um die Differenz zu X-Server-Timestamp und signieren Sie erneut mit einer neuen Nonce. | ja |
| 401 | signature_invalid | X-Signature passt nicht zum Signatur-Secret des Schlüssels, oder X-Nonce ist fehlerhaft. | Signieren Sie Zeitstempel, Nonce, Methode und Pfad (ohne Query-String), durch Zeilenumbrüche verbunden, mit dem aktuellen Secret aus Konsole → API-Schlüssel. | nein |
| 401 | signature_replayed | Diese X-Nonce wurde mit diesem Schlüssel in den letzten 10 Minuten bereits verwendet. | Erzeugen Sie für jede Anfrage eine neue zufällige Nonce, auch bei Wiederholungen. | ja |
| 403 | access_denied | Die IP des Aufrufers liegt außerhalb der IP-Allowlist des Schlüssels. | Fügen Sie die ausgehende Adresse unter Konsole → API-Schlüssel hinzu oder entfernen Sie die Allowlist bei Schlüsseln ohne feste IP. | nein |
| 403 | keine | Konto gesperrt, oder der Schlüssel darf dieses Modell bzw. diesen Kanal nicht nutzen. | Prüfen Sie zuerst die Modell-Allowlist des Schlüssels; ist sie unbeschränkt, eröffnen Sie ein Ticket — dann ist das Konto selbst gesperrt. | nein |
| 400 | unsupported_endpoint | Das Modell existiert, bedient diesen Pfad aber nicht. Die Meldung listet die Endpunkte auf, die es bedient. | Verlegen Sie den Aufruf auf einen der aufgeführten Endpunkte — Bild-Task-Modelle antworten nicht auf /v1/chat/completions. | nein |
| 400 | read_request_body_failed | Der Anfrage-Body konnte nicht gelesen werden — abgeschnittener Upload, falscher Content-Type oder ein vorzeitig beendeter Stream. | Senden Sie einen vollständigen Body mit passendem Content-Type. Dieselbe fehlerhafte Anfrage scheitert beim erneuten Senden auf dieselbe Weise. | nein |
| 402 | insufficient_user_quota | Das Wallet-Guthaben deckt den für diesen Aufruf vorab reservierten Betrag nicht. | Laden Sie unter Konsole → Wallet auf und versuchen Sie es dann erneut. | nein |
| 429 | insufficient_user_quota | Das tägliche Ausgabenlimit des Kontos ist erreicht. Gleicher Status wie bei Drosselung, gegenteilige Bedeutung — Backoff hilft hier nicht. | Erhöhen oder entfernen Sie das Limit in den Kontoeinstellungen, oder machen Sie morgen weiter. Werten Sie den code aus, nicht nur 429. | nein |
| 429 | keine | RPM/RPD-Drosselung pro Schlüssel oder eine Parallelitätssperre. Der Body ist leer; Retry-After enthält die Wartezeit. | Warten Sie Retry-After Sekunden ab und versuchen Sie es dann mit exponentiellem Backoff und Jitter erneut. | ja |
| 413 | read_request_body_failed | Der Anfrage-Body überschreitet die zulässige Größe. Das Limit auf Transportebene bricht mit leerem Body ab. | Verkleinern Sie den Payload — laden Sie Dateien über /v1/media/upload-policy hoch und senden Sie die URL statt Base64. | nein |
| 422 | invalid_request | Kein Kanal unterstützt diesen Medienmodus für das Modell — zum Beispiel Bild-zu-Video auf einer Route, die nur Text-zu-Video kann. | Prüfen Sie die unterstützten Modi auf der Modellkarte und wechseln Sie Modell oder Modus. | nein |
| 503 | model_not_found | Unbekannter Modellname, oder derzeit bedient ihn kein funktionsfähiger Kanal. Beachten Sie den Status: Ein unbekanntes Modell ergibt 503, nicht 404. | Prüfen Sie den Namen anhand von /v1/models und versuchen Sie es erneut — das Failover findet beim nächsten Versuch meist einen funktionsfähigen Kanal. | ja |
| 500 | sensitive_words_detected | Der Prompt wurde vom Inhaltsfilter abgefangen, bevor er das Gateway verlassen hat. | Ändern Sie den Prompt. Derselbe Text scheitert bei jedem Versuch. | nein |
| 500 | count_token_failed | Das Gateway konnte die Tokens für die Preisberechnung nicht schätzen, meist wegen eines fehlerhaften messages-Arrays. | Validieren Sie das messages-Array; ist es korrekt aufgebaut, versuchen Sie es erneut und eröffnen Sie ein Ticket mit der Anfrage-ID. | ja |
| 500 | invalid_request | Die Anfrage ließ sich nicht in ein Format parsen, das dieser Endpunkt akzeptiert. Beachten Sie den Status — ein Body, den das Gateway nicht lesen kann, wird als 500 gemeldet, nicht als 400. | Validieren Sie das JSON vor dem erneuten Versuch anhand der Endpunkt-Referenz. | nein |
| 502 | do_request_failed | Der Anbieter war nicht erreichbar oder lieferte einen Fehler, den das Gateway auch per Failover nicht beheben konnte. | Mit Backoff erneut versuchen — der nächste Versuch landet meist auf einem anderen Kanal. | ja |
| 504 | do_request_failed | Der Anbieter hat nicht innerhalb der Frist des Gateways geantwortet. | Mit Backoff erneut versuchen; lange Prompts und ausgelastete Anbieter führen häufiger zu Timeouts. | ja |
| 499 | do_request_failed | Ihr Client hat die Verbindung geschlossen, bevor die Antwort eintraf — meist hat Ihr eigenes HTTP-Timeout mitten im Stream gegriffen. | Erhöhen Sie das Lese-Timeout des Clients. Lange Generierungen brauchen Minuten, nicht den Standardwert der meisten HTTP-Bibliotheken. | nein |
Fehler der Idempotenzprüfung
Diese stammen von der Replay-Prüfung vor dem Relay und nutzen daher die flache Struktur {code, error} mit code in Großbuchstaben statt der OpenAI-Fehlerstruktur.
| HTTP | Code | Bedeutung | Was tun | Wiederholen? |
|---|---|---|---|---|
| 400 | IDEMPOTENCY_STREAM_UNSUPPORTED | Idempotency-Key wurde bei einer Streaming-Anfrage gesendet. | Lassen Sie den Header bei Streaming-Aufrufen weg, oder setzen Sie stream:false, wenn Sie Replay-Schutz brauchen. | nein |
| 400 | IDEMPOTENCY_BODY_UNREADABLE | Der Body konnte für das Idempotenz-Register nicht gehasht werden. | Senden Sie einen normalen JSON- oder Multipart-Body — Chunked-Bodys, die sich nicht puffern lassen, werden abgelehnt. | nein |
| 409 | IDEMPOTENCY_PAYLOAD_CONFLICT | Dieser Schlüssel wurde bereits mit einem anderen Body, Pfad oder Query verwendet. | Erzeugen Sie einen neuen Schlüssel. Ein Schlüssel kennzeichnet genau eine Anfrage, nicht eine ganze Wiederholungsschleife. | nein |
| 409 | IDEMPOTENCY_IN_PROGRESS | Die erste Anfrage mit diesem Schlüssel läuft noch. | Warten Sie Retry-After (2 s) ab und versuchen Sie es mit demselben Schlüssel erneut — Sie erhalten die ursprüngliche Antwort. | ja |
| 409 | IDEMPOTENCY_RESULT_UNREPLAYABLE | Die ursprüngliche Anfrage ist abgeschlossen, ihre Antwort wurde jedoch versiegelt und kann nicht erneut ausgeliefert werden. | Nicht blind erneut senden. Gleichen Sie zuerst über den Task-Endpunkt oder Ihre eigenen Aufzeichnungen ab. | nein |
| 503 | IDEMPOTENCY_UNAVAILABLE | Das Idempotenz-Register ist nicht verfügbar, daher blockiert die Prüfung lieber, statt eine doppelte Belastung zu riskieren. | Mit Backoff erneut versuchen — die Anfrage wurde nicht ausgeführt. | ja |
Fehler asynchroner Tasks
Video-, Bild-Task-, Musik- und 3D-Endpunkte antworten mit ihrer eigenen flachen Struktur — {code, message, data} — beim Einreichen, Abfragen und Abbrechen gleichermaßen. Eine Eigenheit, die Sie einplanen sollten: Das Abfragen einer unbekannten Task-ID ergibt 400, das Abbrechen 404.
| HTTP | Code | Bedeutung | Was tun | Wiederholen? |
|---|---|---|---|---|
| 400 | task_not_exist | Abfragen oder Remixen einer Task-ID, die nicht existiert oder zu einem anderen Konto gehört. Beachten Sie den Status — das Abfragen eines unbekannten Tasks ergibt 400, das Abbrechen dagegen 404. | Speichern Sie task_id beim Einreichen und fragen Sie mit genau diesem Wert ab. | nein |
| 400 | invalid_request | Ein Pflichtfeld fehlt — prompt bei einem Remix, task_id bei einem Abruf. | Korrigieren Sie Body oder Pfad, bevor Sie es erneut versuchen. | nein |
| 400 | task_channel_disable | Ein Folge-Task (Remix, Verlängerung) ist an den Kanal des ursprünglichen Tasks gebunden, und dieser Kanal ist jetzt deaktiviert. | Reichen Sie einen neuen Task ein, statt vom alten abzuleiten. | nein |
| 400 | invalid_source_task | Der Quell-Task hat den falschen Typ für diese Operation — zum Beispiel eine Video-ID, die an eine 3D-Fortsetzung übergeben wird. | Prüfen Sie, ob die Quell-ID aus derselben Fähigkeitsfamilie stammt. | nein |
| 404 | task_not_exist | Abbrechen einer Task-ID, die nicht existiert oder nicht Ihnen gehört. | Nichts abzubrechen — behandeln Sie den Task als bereits entfernt. | nein |
| 409 | task_not_cancellable | Der Task hat bereits einen Endzustand erreicht, es gibt also nichts abzubrechen. | Fragen Sie noch einmal ab und verwenden Sie das Ergebnis, das Sie bereits bezahlt haben. | nein |
| 501 | task_cancel_not_supported | Der Anbieter hinter diesem Task bietet keine API zum Abbrechen. Die meisten tun das nicht — Abbrechen ist die Ausnahme, nicht die Regel. | Lassen Sie den Task zu Ende laufen. Gestalten Sie die UI so, dass Abbrechen ein Versuch ist, keine Garantie. | nein |
| 502 | cancel_task_failed | Der Anbieter war beim Abbrechen nicht erreichbar. Der Task ist unverändert. | Einmal erneut versuchen, sonst den Task zu Ende laufen lassen. | ja |
Immer noch nicht gelöst?
Senden Sie uns die Anfrage-ID oder die Fehlermeldung, und wir sehen uns das an.