Obsługa błędów
Zapytanie, które się nie powiedzie, zwraca kod statusu HTTP i treść JSON z typem błędu i komunikatem. Ta strona wymienia każdy status, pokazuje treść błędu w każdym formacie API i mówi, które błędy warto wysłać ponownie.
SDK zgłaszają obiekt błędu, który niesie kod statusu i pola treści. To zapytanie podaje model, który nie istnieje:
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"}]
}' Zwraca status 400 i tę treść:
{
"error": {
"type": "invalid_request_error",
"message": "unknown model: no-such-model"
}
} Treść błędu
Są dwie treści błędu. /v1/messages odpowiada w formacie Anthropic; każdy inny endpoint odpowiada w formacie OpenAI. W obu szczegóły są w error.type i error.message, więc jeden czytnik obsługuje wszystkie endpointy.
{
"error": {
"type": "authentication_error",
"message": "Invalid API key",
"code": "invalid_api_key",
"param": null
}
} {
"type": "error",
"error": {
"type": "authentication_error",
"message": "Invalid API key"
}
} | Pole | Typ | Opis |
|---|---|---|
error.type | string | Rodzaj błędu. Zobacz niżej Typy błędów. |
error.message | string | Zdanie dla człowieka. Teksty w tabelach poniżej są stałe aż do części zmiennych, pokazanych jako <id>, <n> lub …. |
error.code | string | null | Obecne w niektórych błędach formatu OpenAI: invalid_api_key z 401, request_too_large z 413, context_length_exceeded, gdy rozmowa jest zbyt długa, poza tym null lub brak. Najpierw decyduj według statusu i error.type, a code traktuj jako dodatek. |
error.param | null | Obecne w tych samych błędach co code. Zawsze null. |
type | string | Tylko format Anthropic. Zawsze error. |
Typy błędów
| Typ | Statusy | Opis |
|---|---|---|
invalid_request_error | 400, 413, 415, 422 | Samo zapytanie jest błędne: pole, treść, id modelu lub wejście, którego model nie przyjmuje. Zmień zapytanie, zanim wyślesz je ponownie. |
authentication_error | 401 | Klucz API jest nieobecny, nieznany lub unieważniony. |
not_found_error | 404 | Ścieżka nie istnieje. |
rate_limit_error | 429 | Saldo nie pokrywa zapytania, zatrzymała je ochrona przed floodem albo model jest zajęty. Komunikat mówi, które z tych. |
api_error | 500, 502, 503, 504 | Zapytanie było poprawne, a awaria jest po naszej stronie. Wyślij je ponownie. |
server_error | 500 | Zapytanie było poprawne, a awaria jest po naszej stronie. Wyślij je ponownie. |
Kody statusu
| Status | Typ | Komunikat | Kiedy |
|---|---|---|---|
400 | invalid_request_error | Failed to parse the request body as JSON: … | Treść nie jest prawidłowym JSON. |
400 | invalid_request_error | unknown model: <id> | model nie jest opublikowanym id. W /v1/responses także wtedy, gdy brakuje model. |
400 | invalid_request_error | No user message provided | Poziomy Shannon: zapytanie nie zawiera tekstu użytkownika. |
400 | invalid_request_error | <id> does not accept image input | Obraz wysłano do hostowanego modelu open-weight bez obsługi obrazu na wejściu. |
400 | invalid_request_error | audio and video input are not supported by any hosted model | Część audio lub wideo wysłano do hostowanego modelu open-weight. |
400 | invalid_request_error | <id> does not accept response_format | Zażądano wyjścia strukturyzowanego od hostowanego modelu open-weight, który go nie ma. |
400 | invalid_request_error | <id> does not accept response_format json_schema; use json_object | Schemat JSON wysłano do hostowanego modelu open-weight, który przyjmuje tylko json_object. |
400 | invalid_request_error | unknown reasoning effort '<value>'; expected off, low, medium or high | Wysiłek rozumowania nie jest jedną z wymienionych wartości. Hostowane modele open-weight. |
400 | invalid_request_error | count_tokens is available for the hosted open models; unknown model: <id> | Zażądano liczenia tokenów dla modelu spoza hostowanych modeli open-weight. |
401 | authentication_error | Missing authentication | Nie wysłano klucza API. |
401 | authentication_error | Invalid API key | Klucz jest nieznany, został zastąpiony nowszym albo unieważniony. |
404 | not_found_error | Not Found | Ścieżka nie istnieje, na przykład /v1/embeddings. |
405 | api_error | Method Not Allowed | Ścieżka istnieje, ale nie z tą metodą HTTP. |
413 | invalid_request_error | Failed to buffer the request body: … | Treść zapytania jest większa niż 32 MiB. |
415 | invalid_request_error | Expected request with `Content-Type: application/json` | Brakuje nagłówka Content-Type albo nie ma on wartości application/json. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | Brakuje wymaganego pola, takiego jak messages, albo pole ma zły typ JSON. |
429 | rate_limit_error | Quota exceeded. Upgrade your plan at shannon-ai.com/plan | Limit wyjścia zapytania jest większy niż to, co zostało z Twojego salda. |
429 | rate_limit_error | Too many requests. Retry in <n>s. | Ochrona przed floodem: ponad 120 zapytań w ciągu jednej minuty na Twoim koncie. <n> to liczba sekund do końca minuty. |
429 | rate_limit_error | Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan | shannon-coder-1 w /v1/chat/completions i /v1/messages: wywołania Shannon Coder w Twoim planie na bieżące 4-godzinne okno zostały wykorzystane. |
429 | rate_limit_error | Shannon routes are temporarily busy. Please retry. | Model jest w tej chwili zajęty. |
500 | server_error | The model backend failed to answer. Please retry. | Model nie wygenerował odpowiedzi. |
500 | server_error | An internal error occurred. Please retry. | Wewnętrzna awaria podczas przygotowywania zapytania. |
502 | api_error | The model backend failed to answer. Please retry. | Model nie wygenerował odpowiedzi. Rodzina Shannon 3 i hostowane modele open-weight. |
503 | api_error | Could not verify your quota right now. Please retry. | Nie udało się w tej chwili odczytać Twojego salda. |
503 | api_error | token counting is temporarily unavailable for this model | Liczenie tokenów nie jest w tej chwili dostępne dla tego modelu. |
504 | api_error | upstream timed out | Hostowany model open-weight potrzebował więcej czasu niż wynosi limit czasu zapytania bez streamingu. |
Kontrole przebiegają w tej kolejności: klucz API, treść zapytania, id modelu, ochrona przed floodem, saldo. Zapytanie z błędnym kluczem i błędną treścią zwraca więc 401. Zapytanie odrzucone ze statusem 4xx nie jest rozliczane.
Saldo i ochrona przed floodem
Każde zapytanie jest obsługiwane jednakowo: nie ma poziomów limitów szybkości ani osobnej kwoty API. Odpowiedź 429 mogą jednak dać dwie rzeczy, obie z typem rate_limit_error. Komunikat je rozróżnia.
Saldo
Twoje saldo to dzisiejszy limit planu, który odnawia się o 00:00 UTC, a po nim zakupione środki. Czat i API czerpią z tego samego salda.
Każde zapytanie najpierw odkłada swój limit wyjścia: max_tokens lub max_output_tokens w /v1/responses, 4,096, gdy nie wyślesz żadnego. Gdy odpowiedź jest kompletna, odłożona kwota jest zastępowana tym, co zapytanie zużyło. Jeśli kwota nie mieści się w pozostałym saldzie, zapytanie zwraca Quota exceeded, zanim model zacznie pracę.
Zapytania działające w tym samym czasie odkładają każde swoją kwotę. Wiele równoległych zapytań z wysokim max_tokens może więc na chwilę wyczerpać saldo, nawet gdy każda odpowiedź jest krótka. Niższe max_tokens odkłada mniej.
Ochrona przed floodem
Ochrona przed floodem dopuszcza 120 zapytań na minutę na konto. Liczy konto, nie klucz, łącznie dla /v1/chat/completions, /v1/responses i /v1/messages. Minuta zaczyna się od Twojego pierwszego zapytania.
Zapytanie numer 121 w tej minucie zwraca Too many requests. Retry in <n>s. i nic nie kosztuje. Odrzucone zapytania też się liczą, więc klient, który wysyła ponownie bez czekania, pozostaje odrzucany do końca minuty. Poczekaj <n> sekund z komunikatu.
Zapytania wysyłane równolegle nie są odrzucane za to, że są równoległe. Czekają w kolejce i są obsługiwane po kolei.
GET /v1/models i endpointy liczenia tokenów nie są liczone.
Więcej o limitach i saldzie oraz o tym, gdzie zobaczyć własne saldo i użycie: Limity i saldo Keys & usage
Co ponawiać
Wysyłaj zapytanie ponownie tylko wtedy, gdy to samo zapytanie może się powieść. SDK OpenAI i Anthropic same ponawiają odpowiedzi 429 i 5xx dwa razy; ustaw max_retries (maxRetries w JavaScript), aby to zmienić.
| Błąd | Wysłać ponownie? | Jak |
|---|---|---|
400, 404, 405, 413, 415, 422 | Nie. Najpierw popraw zapytanie. | Komunikat wskazuje pole, model lub wejście, które jest błędne. |
401 | Nie. Użyj ważnego klucza. | Utwórz lub wymień klucz na stronie Keys & usage. |
429 Too many requests | Tak, po oczekiwaniu. | Poczekaj liczbę sekund z komunikatu, a potem wyślij to samo zapytanie. |
429 Quota exceeded | Nie w tej postaci. | Obniż max_tokens, poczekaj na dzienny reset o 00:00 UTC albo dodaj środki. |
429 Shannon Coder call quota reached | Tak, w następnym oknie. | Okna zaczynają się co 4 godziny od 00:00 UTC. Inny model odpowiada od razu. |
429 … temporarily busy | Tak. | Poczekaj kilka sekund, a przy każdej kolejnej próbie dłużej. |
500, 502, 503 | Tak. | Poczekaj 1 sekundę, potem 2, 4 i 8. Przerwij po kilku próbach i zgłoś id zapytania. |
504 | Tak. | Wyślij je z stream ustawionym na true: limit czasu dotyczy odpowiedzi bez streamingu. |
Pętla ponawiania zgodna z tą tabelą:
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); Błędy wewnątrz strumienia
Strumień ma status 200 od pierwszego bajtu. Awaria po tym momencie przychodzi jako zdarzenie wewnątrz strumienia: fragment z polem error w /v1/chat/completions, zdarzenie error w /v1/messages, zdarzenie response.failed lub error w /v1/responses. Zdarzenia kończące strumień nadal następują. Potraktuj taką odpowiedź jako nieudaną i wyślij zapytanie ponownie, jak przy statusie 5xx.
Rozmowa dłuższa niż okno kontekstu modelu jest zgłaszana w ten sposób, z typem invalid_request_error i kodem context_length_exceeded. Skróć rozmowę, zanim wyślesz ją ponownie.
Dokładne ramki opisano przy streamingu. Streaming
Id zapytań
Każda odpowiedź, także z błędem, ma nagłówek x-request-id. Jest to wartość, którą wysłałeś we własnym nagłówku x-request-id, albo 12 znaków szesnastkowych, gdy nie wysłałeś żadnej. Zapisuj go w logu i podawaj, gdy prosisz o pomoc w sprawie zapytania.