Fehlerbehandlung
Eine Anfrage, die fehlschlägt, gibt einen HTTP-Statuscode und einen JSON-Body mit einem Fehlertyp und einer Nachricht zurück. Diese Seite listet jeden Status auf, zeigt den Fehler-Body jedes API-Formats und sagt, welche Fehler sich zum erneuten Senden lohnen.
Die SDKs lösen ein Fehlerobjekt aus, das den Statuscode und die Felder des Bodys enthält. Diese Anfrage nennt ein Modell, das nicht existiert:
import openai
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.shannon-ai.com/v1",
)
try:
client.chat.completions.create(
model="no-such-model",
messages=[{"role": "user", "content": "Hello"}],
)
except openai.APIStatusError as error:
print(error.status_code) # 400
print(error.type) # invalid_request_error
print(error.body["message"]) # unknown model: no-such-model
print(error.response.headers.get("x-request-id")) # the id of this request import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.shannon-ai.com/v1",
});
try {
await client.chat.completions.create({
model: "no-such-model",
messages: [{ role: "user", content: "Hello" }],
});
} catch (error) {
if (!(error instanceof OpenAI.APIError)) throw error;
console.log(error.status); // 400
console.log(error.type); // invalid_request_error
console.log(error.error.message); // unknown model: no-such-model
} curl -i https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "no-such-model",
"messages": [{"role": "user", "content": "Hello"}]
}' Sie gibt den Status 400 und diesen Body zurück:
{
"error": {
"type": "invalid_request_error",
"message": "unknown model: no-such-model"
}
} Fehler-Body
Es gibt zwei Bodys. /v1/messages antwortet im Anthropic-Format; jeder andere Endpunkt antwortet im OpenAI-Format. In beiden stehen die Details in error.type und error.message, sodass ein Parser für alle Endpunkte genügt.
{
"error": {
"type": "authentication_error",
"message": "Invalid API key",
"code": "invalid_api_key",
"param": null
}
} {
"type": "error",
"error": {
"type": "authentication_error",
"message": "Invalid API key"
}
} | Feld | Typ | Beschreibung |
|---|---|---|
error.type | string | Die Art des Fehlers. Siehe Fehlertypen weiter unten. |
error.message | string | Ein Satz für Menschen. Die Texte in den Tabellen unten sind bis auf ihre variablen Teile stabil, die als <id>, <n> oder … dargestellt sind. |
error.code | string | null | Bei einigen Fehlern im OpenAI-Format vorhanden: invalid_api_key bei 401, request_too_large bei 413, context_length_exceeded, wenn eine Konversation zu lang ist, sonst null oder nicht vorhanden. Entscheiden Sie zuerst anhand von Status und error.type und behandeln Sie code als Zusatz. |
error.param | null | Bei denselben Fehlern vorhanden wie code. Immer null. |
type | string | Nur im Anthropic-Format. Immer error. |
Fehlertypen
| Typ | Status | Beschreibung |
|---|---|---|
invalid_request_error | 400, 413, 415, 422 | Die Anfrage selbst ist falsch: ein Feld, der Body, die Modell-ID oder ein Input, den das Modell nicht annimmt. Ändern Sie die Anfrage, bevor Sie sie erneut senden. |
authentication_error | 401 | Der API-Key fehlt, ist unbekannt oder widerrufen. |
not_found_error | 404 | Der Pfad existiert nicht. |
rate_limit_error | 429 | Das Guthaben deckt die Anfrage nicht, der Flood-Schutz hat sie gestoppt oder das Modell ist ausgelastet. Die Nachricht sagt, was zutrifft. |
api_error | 500, 502, 503, 504 | Die Anfrage war in Ordnung, und der Fehler liegt auf unserer Seite. Senden Sie sie erneut. |
server_error | 500 | Die Anfrage war in Ordnung, und der Fehler liegt auf unserer Seite. Senden Sie sie erneut. |
Statuscodes
| Status | Typ | Nachricht | Wann |
|---|---|---|---|
400 | invalid_request_error | Failed to parse the request body as JSON: … | Der Body ist kein gültiges JSON. |
400 | invalid_request_error | unknown model: <id> | model ist keine veröffentlichte ID. Bei /v1/responses auch, wenn model fehlt. |
400 | invalid_request_error | No user message provided | Shannon-Stufen: Die Anfrage enthält keinen Nutzertext. |
400 | invalid_request_error | <id> does not accept image input | Ein Bild wurde an ein gehostetes Open-Weight-Modell ohne Bild-Input gesendet. |
400 | invalid_request_error | audio and video input are not supported by any hosted model | Ein Audio- oder Videoteil wurde an ein gehostetes Open-Weight-Modell gesendet. |
400 | invalid_request_error | <id> does not accept response_format | Strukturierte Ausgabe wurde von einem gehosteten Open-Weight-Modell verlangt, das sie nicht hat. |
400 | invalid_request_error | <id> does not accept response_format json_schema; use json_object | Ein JSON-Schema wurde an ein gehostetes Open-Weight-Modell gesendet, das nur json_object annimmt. |
400 | invalid_request_error | unknown reasoning effort '<value>'; expected off, low, medium or high | Der Reasoning-Aufwand ist keiner der aufgeführten Werte. Gehostete Open-Weight-Modelle. |
400 | invalid_request_error | count_tokens is available for the hosted open models; unknown model: <id> | Das Token-Zählen wurde für ein Modell außerhalb der gehosteten Open-Weight-Modelle angefordert. |
401 | authentication_error | Missing authentication | Es wurde kein API-Key gesendet. |
401 | authentication_error | Invalid API key | Der Key ist unbekannt, wurde durch einen neueren ersetzt oder wurde widerrufen. |
404 | not_found_error | Not Found | Der Pfad existiert nicht, zum Beispiel /v1/embeddings. |
405 | api_error | Method Not Allowed | Der Pfad existiert, aber nicht mit dieser HTTP-Methode. |
413 | invalid_request_error | Failed to buffer the request body: … | Der Request-Body ist größer als 32 MiB. |
415 | invalid_request_error | Expected request with `Content-Type: application/json` | Der Header Content-Type fehlt oder ist nicht application/json. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | Ein erforderliches Feld fehlt, etwa messages, oder ein Feld hat den falschen JSON-Typ. |
429 | rate_limit_error | Quota exceeded. Upgrade your plan at shannon-ai.com/plan | Das Output-Limit der Anfrage ist größer als das, was von Ihrem Guthaben übrig ist. |
429 | rate_limit_error | Too many requests. Retry in <n>s. | Flood-Schutz: mehr als 120 Anfragen in einer Minute auf Ihrem Konto. <n> ist die Anzahl Sekunden bis zum Ende der Minute. |
429 | rate_limit_error | Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan | shannon-coder-1 bei /v1/chat/completions und /v1/messages: Die Shannon-Coder-Aufrufe Ihres Plans für das aktuelle 4-Stunden-Fenster sind aufgebraucht. |
429 | rate_limit_error | Shannon routes are temporarily busy. Please retry. | Das Modell ist im Moment ausgelastet. |
500 | server_error | The model backend failed to answer. Please retry. | Das Modell hat keine Antwort erzeugt. |
500 | server_error | An internal error occurred. Please retry. | Ein interner Fehler bei der Vorbereitung der Anfrage. |
502 | api_error | The model backend failed to answer. Please retry. | Das Modell hat keine Antwort erzeugt. Shannon-3-Familie und gehostete Open-Weight-Modelle. |
503 | api_error | Could not verify your quota right now. Please retry. | Ihr Guthaben konnte im Moment nicht gelesen werden. |
503 | api_error | token counting is temporarily unavailable for this model | Das Token-Zählen ist für dieses Modell im Moment nicht verfügbar. |
504 | api_error | upstream timed out | Ein gehostetes Open-Weight-Modell hat bei einer nicht gestreamten Anfrage länger als das Zeitlimit gebraucht. |
Die Prüfungen laufen in dieser Reihenfolge: API-Key, Request-Body, Modell-ID, Flood-Schutz, Guthaben. Eine Anfrage mit falschem Key und falschem Body gibt daher 401 zurück. Eine Anfrage, die mit einem 4xx-Status abgelehnt wird, wird nicht berechnet.
Guthaben und Flood-Schutz
Jede Anfrage wird gleich bedient: Es gibt keine Ratenstufen und kein separates API-Kontingent. Zwei Dinge können trotzdem mit 429 antworten, beide mit dem Typ rate_limit_error. Die Nachricht unterscheidet sie.
Guthaben
Ihr Guthaben besteht aus dem heutigen Plan-Kontingent, das um 00:00 UTC zurückgesetzt wird, und danach aus Ihrem gekauften Guthaben. Chat und API ziehen vom selben Guthaben ab.
Jede Anfrage legt zuerst ihr Output-Limit zurück: max_tokens oder bei /v1/responses max_output_tokens, 4,096, wenn Sie keines von beiden senden. Ist die Antwort vollständig, wird der zurückgelegte Betrag durch das ersetzt, was die Anfrage verbraucht hat. Passt der Betrag nicht in das verbleibende Guthaben, gibt die Anfrage Quota exceeded zurück, bevor das Modell startet.
Anfragen, die gleichzeitig laufen, legen jeweils ihren eigenen Betrag zurück. Viele parallele Anfragen mit hohem max_tokens können das Guthaben daher kurzzeitig aufbrauchen, auch wenn jede Antwort kurz ist. Ein niedrigeres max_tokens legt weniger zurück.
Flood-Schutz
Der Flood-Schutz erlaubt 120 Anfragen pro Minute und Konto. Er zählt das Konto, nicht den Key, über /v1/chat/completions, /v1/responses und /v1/messages zusammen. Die Minute beginnt mit Ihrer ersten Anfrage.
Anfrage Nummer 121 innerhalb dieser Minute gibt Too many requests. Retry in <n>s. zurück und kostet nichts. Abgelehnte Anfragen zählen ebenfalls, daher bleibt ein Client, der ohne Wartezeit erneut sendet, abgelehnt, bis die Minute vorbei ist. Warten Sie die <n> Sekunden aus der Nachricht.
Parallel gesendete Anfragen werden nicht deshalb abgelehnt, weil sie parallel sind. Sie warten in der Schlange und werden der Reihe nach beantwortet.
GET /v1/models und die Endpunkte zum Token-Zählen werden nicht mitgezählt.
Mehr zu Limits und Guthaben und dazu, wo Sie Ihr eigenes Guthaben und Ihre Nutzung sehen: Limits und Guthaben Keys & Nutzung
Was wiederholt werden sollte
Senden Sie eine Anfrage nur dann erneut, wenn dieselbe Anfrage gelingen kann. Die OpenAI- und Anthropic-SDKs wiederholen Antworten mit 429 und 5xx bereits von selbst zweimal; setzen Sie max_retries (maxRetries in JavaScript), um das zu ändern.
| Fehler | Erneut senden? | Wie |
|---|---|---|
400, 404, 405, 413, 415, 422 | Nein. Korrigieren Sie zuerst die Anfrage. | Die Nachricht nennt das Feld, das Modell oder den Input, der falsch ist. |
401 | Nein. Verwenden Sie einen gültigen Key. | Erstellen oder ersetzen Sie den Key auf der Seite Keys & Nutzung. |
429 Too many requests | Ja, nach der Wartezeit. | Warten Sie die Anzahl Sekunden aus der Nachricht und senden Sie dann dieselbe Anfrage. |
429 Quota exceeded | Nicht so, wie sie ist. | Senken Sie max_tokens, warten Sie auf das tägliche Zurücksetzen um 00:00 UTC oder laden Sie Guthaben auf. |
429 Shannon Coder call quota reached | Ja, im nächsten Fenster. | Die Fenster beginnen alle 4 Stunden ab 00:00 UTC. Ein anderes Modell antwortet sofort. |
429 … temporarily busy | Ja. | Warten Sie einige Sekunden, bei jedem weiteren Versuch länger. |
500, 502, 503 | Ja. | Warten Sie 1 Sekunde, dann 2, 4 und 8. Hören Sie nach einigen Versuchen auf und melden Sie die Request-ID. |
504 | Ja. | Senden Sie sie mit stream auf true: Das Zeitlimit gilt für Antworten, die nicht gestreamt werden. |
Eine Retry-Schleife, die dieser Tabelle folgt:
import re
import time
import openai
from openai import OpenAI
# max_retries=0: this loop does the retrying, the SDK does not add its own.
client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1", max_retries=0)
def create_with_retry(attempts=5, **request):
for attempt in range(attempts):
try:
return client.chat.completions.create(**request)
except openai.RateLimitError as error:
text = str(error)
flood = re.search(r"Retry in (\d+)s", text)
if flood:
time.sleep(int(flood.group(1))) # flood protection: wait out the minute
elif "temporarily busy" in text:
time.sleep(2 ** attempt)
else:
raise # balance: a wait of seconds does not help
except (openai.InternalServerError, openai.APIConnectionError):
time.sleep(2 ** attempt) # 1, 2, 4, 8 seconds
raise RuntimeError("the request failed after all attempts")
response = create_with_retry(
model="shannon-3",
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content) import OpenAI from "openai";
// maxRetries: 0 — this loop does the retrying, the SDK does not add its own.
const client = new OpenAI({ apiKey: "YOUR_API_KEY", baseURL: "https://api.shannon-ai.com/v1", maxRetries: 0 });
const sleep = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000));
async function createWithRetry(request, attempts = 5) {
for (let attempt = 0; attempt < attempts; attempt++) {
try {
return await client.chat.completions.create(request);
} catch (error) {
const text = String(error.message);
const flood = text.match(/Retry in (\d+)s/);
if (error.status === 429 && flood) {
await sleep(Number(flood[1])); // flood protection: wait out the minute
} else if (error.status === 429 && text.includes("temporarily busy")) {
await sleep(2 ** attempt);
} else if (error.status >= 500 || error instanceof OpenAI.APIConnectionError) {
await sleep(2 ** attempt); // 1, 2, 4, 8 seconds
} else {
throw error; // wrong request, wrong key or balance
}
}
}
throw new Error("the request failed after all attempts");
}
const response = await createWithRetry({
model: "shannon-3",
messages: [{ role: "user", content: "Hello" }],
});
console.log(response.choices[0].message.content); Fehler innerhalb eines Streams
Ein Stream hat ab seinem ersten Byte den Status 200. Ein Fehler danach trifft als Event innerhalb des Streams ein: ein Chunk mit einem Member error bei /v1/chat/completions, ein error-Event bei /v1/messages, ein response.failed- oder error-Event bei /v1/responses. Die abschließenden Events des Streams folgen trotzdem. Behandeln Sie eine solche Antwort als fehlgeschlagen und senden Sie die Anfrage erneut, wie bei einem 5xx-Status.
Eine Konversation, die länger als das Kontextfenster des Modells ist, wird auf diese Weise gemeldet, mit dem Typ invalid_request_error und dem Code context_length_exceeded. Kürzen Sie die Konversation, bevor Sie sie erneut senden.
Die genauen Frames sind beim Streaming beschrieben. Streaming
Request-IDs
Jede Antwort, auch bei Fehlern, hat einen Header x-request-id. Er ist der Wert, den Sie in Ihrem eigenen Header x-request-id gesendet haben, oder 12 hexadezimale Zeichen, wenn Sie keinen gesendet haben. Protokollieren Sie ihn und nennen Sie ihn, wenn Sie Hilfe zu einer Anfrage erbitten.