Foutafhandeling
Een aanvraag die mislukt, geeft een HTTP-statuscode terug en een JSON-body met een fouttype en een bericht. Deze pagina toont elke status, laat de foutbody van elk API-formaat zien en zegt welke fouten het waard zijn om opnieuw te versturen.
De SDK's werpen een foutobject op dat de statuscode en de velden van de body bevat. Deze aanvraag noemt een model dat niet bestaat:
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"}]
}' Ze geeft status 400 en deze body terug:
{
"error": {
"type": "invalid_request_error",
"message": "unknown model: no-such-model"
}
} Foutbody
Er zijn twee bodies. /v1/messages antwoordt in het Anthropic-formaat; elk ander endpoint antwoordt in het OpenAI-formaat. In beide staan de details in error.type en error.message, dus één reader volstaat voor alle endpoints.
{
"error": {
"type": "authentication_error",
"message": "Invalid API key",
"code": "invalid_api_key",
"param": null
}
} {
"type": "error",
"error": {
"type": "authentication_error",
"message": "Invalid API key"
}
} | Veld | Type | Beschrijving |
|---|---|---|
error.type | string | Het soort fout. Zie Fouttypen hieronder. |
error.message | string | Een zin voor een mens. De teksten in de tabellen hieronder zijn stabiel op hun variabele delen na, weergegeven als <id>, <n> of …. |
error.code | string | null | Aanwezig bij sommige fouten in het OpenAI-formaat: invalid_api_key bij 401, request_too_large bij 413, context_length_exceeded als een gesprek te lang is, anders null of afwezig. Beslis eerst op basis van de status en error.type en behandel code als extra. |
error.param | null | Aanwezig bij dezelfde fouten als code. Altijd null. |
type | string | Alleen in het Anthropic-formaat. Altijd error. |
Fouttypen
| Type | Statussen | Beschrijving |
|---|---|---|
invalid_request_error | 400, 413, 415, 422 | De aanvraag zelf is fout: een veld, de body, het model-id of een input die het model niet accepteert. Wijzig de aanvraag voordat je haar opnieuw verstuurt. |
authentication_error | 401 | De API-sleutel ontbreekt, is onbekend of is ingetrokken. |
not_found_error | 404 | Het pad bestaat niet. |
rate_limit_error | 429 | Het saldo dekt de aanvraag niet, flood protection heeft haar gestopt, of het model is bezet. Het bericht zegt welke van de drie. |
api_error | 500, 502, 503, 504 | De aanvraag was in orde en de fout ligt aan onze kant. Verstuur haar opnieuw. |
server_error | 500 | De aanvraag was in orde en de fout ligt aan onze kant. Verstuur haar opnieuw. |
Statuscodes
| Status | Type | Bericht | Wanneer |
|---|---|---|---|
400 | invalid_request_error | Failed to parse the request body as JSON: … | De body is geen geldige JSON. |
400 | invalid_request_error | unknown model: <id> | model is geen gepubliceerd id. Op /v1/responses ook als model ontbreekt. |
400 | invalid_request_error | No user message provided | Shannon-tiers: de aanvraag bevat geen gebruikerstekst. |
400 | invalid_request_error | <id> does not accept image input | Er is een afbeelding verzonden naar een gehost open-weight model zonder afbeeldingen als input. |
400 | invalid_request_error | audio and video input are not supported by any hosted model | Er is een audio- of video-onderdeel verzonden naar een gehost open-weight model. |
400 | invalid_request_error | <id> does not accept response_format | Er is gestructureerde output gevraagd van een gehost open-weight model dat die niet heeft. |
400 | invalid_request_error | <id> does not accept response_format json_schema; use json_object | Er is een JSON-schema verzonden naar een gehost open-weight model dat alleen json_object accepteert. |
400 | invalid_request_error | unknown reasoning effort '<value>'; expected off, low, medium or high | De reasoning effort is geen van de vermelde waarden. Gehoste open-weight modellen. |
400 | invalid_request_error | count_tokens is available for the hosted open models; unknown model: <id> | Er is tokentelling gevraagd voor een model buiten de gehoste open-weight modellen. |
401 | authentication_error | Missing authentication | Er is geen API-sleutel verzonden. |
401 | authentication_error | Invalid API key | De sleutel is onbekend, vervangen door een nieuwere of ingetrokken. |
404 | not_found_error | Not Found | Het pad bestaat niet, bijvoorbeeld /v1/embeddings. |
405 | api_error | Method Not Allowed | Het pad bestaat, maar niet met deze HTTP-methode. |
413 | invalid_request_error | Failed to buffer the request body: … | De request-body is groter dan 32 MiB. |
415 | invalid_request_error | Expected request with `Content-Type: application/json` | De Content-Type-header ontbreekt of is niet application/json. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | Een verplicht veld ontbreekt, zoals messages, of een veld heeft het verkeerde JSON-type. |
429 | rate_limit_error | Quota exceeded. Upgrade your plan at shannon-ai.com/plan | De outputlimiet van de aanvraag is groter dan wat er van je saldo over is. |
429 | rate_limit_error | Too many requests. Retry in <n>s. | Flood protection: meer dan 120 aanvragen in één minuut op je account. <n> is het aantal seconden tot de minuut eindigt. |
429 | rate_limit_error | Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan | shannon-coder-1 op /v1/chat/completions en /v1/messages: de Shannon Coder-calls van je plan voor het huidige venster van 4 uur zijn op. |
429 | rate_limit_error | Shannon routes are temporarily busy. Please retry. | Het model is op dit moment bezet. |
500 | server_error | The model backend failed to answer. Please retry. | Het model gaf geen antwoord. |
500 | server_error | An internal error occurred. Please retry. | Een interne fout terwijl de aanvraag werd voorbereid. |
502 | api_error | The model backend failed to answer. Please retry. | Het model gaf geen antwoord. Shannon 3-familie en gehoste open-weight modellen. |
503 | api_error | Could not verify your quota right now. Please retry. | Je saldo kon op dit moment niet worden gelezen. |
503 | api_error | token counting is temporarily unavailable for this model | Tokentelling is voor dit model op dit moment niet beschikbaar. |
504 | api_error | upstream timed out | Een gehost open-weight model deed er langer over dan de tijdslimiet van een aanvraag die niet wordt gestreamd. |
De controles verlopen in deze volgorde: API-sleutel, request-body, model-id, flood protection, saldo. Een aanvraag met een verkeerde sleutel en een verkeerde body geeft daarom 401 terug. Een aanvraag die met een 4xx-status wordt geweigerd, wordt niet gefactureerd.
Saldo en flood protection
Elke aanvraag wordt gelijk behandeld: er zijn geen rate tiers en geen aparte API-quota. Toch kunnen twee dingen 429 als antwoord geven, beide met het type rate_limit_error. Het bericht maakt het onderscheid.
Saldo
Je saldo is de planlimiet van vandaag, die om 00:00 UTC opnieuw begint, en daarna je aangeschafte credit. Chat en API gebruiken hetzelfde saldo.
Elke aanvraag reserveert eerst haar outputlimiet: max_tokens, of max_output_tokens op /v1/responses, 4,096 als je geen van beide meestuurt. Zodra het antwoord compleet is, wordt het gereserveerde bedrag vervangen door wat de aanvraag heeft gebruikt. Als het bedrag niet in het resterende saldo past, geeft de aanvraag Quota exceeded terug voordat het model begint.
Aanvragen die tegelijk lopen, reserveren elk hun eigen bedrag. Veel parallelle aanvragen met een hoge max_tokens kunnen het saldo daarom even opmaken, ook als elk antwoord kort is. Een lagere max_tokens reserveert minder.
Flood protection
Flood protection staat 120 aanvragen per minuut per account toe. Het telt het account, niet de sleutel, over /v1/chat/completions, /v1/responses en /v1/messages samen. De minuut begint met je eerste aanvraag.
Aanvraag nummer 121 binnen die minuut geeft Too many requests. Retry in <n>s. terug en kost niets. Geweigerde aanvragen tellen ook mee, dus een client die zonder te wachten opnieuw verstuurt, blijft geweigerd tot de minuut voorbij is. Wacht de <n> seconden uit het bericht.
Aanvragen die parallel worden verstuurd, worden niet geweigerd omdat ze parallel zijn. Ze wachten in de rij en worden om de beurt beantwoord.
GET /v1/models en de endpoints voor tokentelling worden niet meegeteld.
Meer over limieten en saldo, en waar je je eigen saldo en gebruik ziet: Limieten en saldo Sleutels en gebruik
Wat je opnieuw moet proberen
Verstuur een aanvraag alleen opnieuw als dezelfde aanvraag kan slagen. De OpenAI- en Anthropic-SDK's proberen 429- en 5xx-antwoorden zelf al twee keer opnieuw; stel max_retries (maxRetries in JavaScript) in om dat te wijzigen.
| Fout | Opnieuw versturen? | Hoe |
|---|---|---|
400, 404, 405, 413, 415, 422 | Nee. Corrigeer eerst de aanvraag. | Het bericht noemt het veld, het model of de input die fout is. |
401 | Nee. Gebruik een geldige sleutel. | Maak de sleutel aan of vervang hem op de pagina Sleutels en gebruik. |
429 Too many requests | Ja, na het wachten. | Wacht het aantal seconden uit het bericht en verstuur dan dezelfde aanvraag. |
429 Quota exceeded | Niet zoals ze is. | Verlaag max_tokens, wacht op de dagelijkse reset om 00:00 UTC, of voeg credit toe. |
429 Shannon Coder call quota reached | Ja, in het volgende venster. | De vensters beginnen elke 4 uur vanaf 00:00 UTC. Een ander model antwoordt direct. |
429 … temporarily busy | Ja. | Wacht een paar seconden, bij elke volgende poging langer. |
500, 502, 503 | Ja. | Wacht 1 seconde, dan 2, 4 en 8. Stop na een paar pogingen en meld het request-id. |
504 | Ja. | Verstuur haar met stream op true: de tijdslimiet geldt voor antwoorden die niet worden gestreamd. |
Een retry-lus die deze tabel volgt:
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); Fouten binnen een stream
Een stream heeft vanaf zijn eerste byte status 200. Een fout daarna komt binnen als event in de stream: een chunk met een error-lid op /v1/chat/completions, een error-event op /v1/messages, een response.failed- of error-event op /v1/responses. De afsluitende events van de stream volgen nog steeds. Behandel zo'n antwoord als mislukt en verstuur de aanvraag opnieuw, zoals bij een 5xx-status.
Een gesprek dat langer is dan het contextvenster van het model, wordt op deze manier gemeld, met het type invalid_request_error en de code context_length_exceeded. Verkort het gesprek voordat je het opnieuw verstuurt.
De exacte frames worden beschreven bij streaming. Streaming
Request-id's
Elk antwoord, ook bij fouten, heeft een x-request-id-header. Dat is de waarde die je in je eigen x-request-id-header hebt meegestuurd, of 12 hexadecimale tekens als je niets hebt meegestuurd. Log hem en vermeld hem als je hulp vraagt bij een aanvraag.