Обробка помилок
Запит, що завершується помилкою, повертає код статусу HTTP і тіло JSON з типом помилки та повідомленням. Ця сторінка перелічує кожен статус, показує тіло помилки в кожному форматі API і каже, які помилки варто повторювати.
SDK піднімають об'єкт помилки, що містить код статусу та поля тіла. Цей запит називає модель, якої не існує:
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"}]
}' Він повертає статус 400 і таке тіло:
{
"error": {
"type": "invalid_request_error",
"message": "unknown model: no-such-model"
}
} Тіло помилки
Є два тіла. /v1/messages відповідає у форматі Anthropic; кожен інший ендпоінт відповідає у форматі OpenAI. В обох подробиці містяться в error.type і error.message, тож один обробник обслуговує всі ендпоінти.
{
"error": {
"type": "authentication_error",
"message": "Invalid API key",
"code": "invalid_api_key",
"param": null
}
} {
"type": "error",
"error": {
"type": "authentication_error",
"message": "Invalid API key"
}
} | Поле | Тип | Опис |
|---|---|---|
error.type | string | Вид помилки. Див. нижче «Типи помилок». |
error.message | string | Речення для людини. Тексти в таблицях нижче стабільні з точністю до змінних частин, показаних як <id>, <n> або …. |
error.code | string | null | Є в деяких помилках формату OpenAI: invalid_api_key з 401, request_too_large з 413, context_length_exceeded, коли розмова задовга, інакше null або відсутнє. Спершу орієнтуйтеся на статус і error.type, а code вважайте додатковим. |
error.param | null | Є в тих самих помилках, що й code. Завжди null. |
type | string | Лише формат Anthropic. Завжди error. |
Типи помилок
| Тип | Статуси | Опис |
|---|---|---|
invalid_request_error | 400, 413, 415, 422 | Сам запит неправильний: поле, тіло, id моделі або вхід, якого модель не приймає. Змініть запит, перш ніж надсилати знову. |
authentication_error | 401 | API-ключ відсутній, невідомий або відкликаний. |
not_found_error | 404 | Шляху не існує. |
rate_limit_error | 429 | Баланс не покриває запит, його зупинив захист від флуду, або модель зайнята. Повідомлення каже, що саме. |
api_error | 500, 502, 503, 504 | Запит був правильний, а збій на нашому боці. Надішліть його знову. |
server_error | 500 | Запит був правильний, а збій на нашому боці. Надішліть його знову. |
Коди статусу
| Статус | Тип | Повідомлення | Коли |
|---|---|---|---|
400 | invalid_request_error | Failed to parse the request body as JSON: … | Тіло не є коректним JSON. |
400 | invalid_request_error | unknown model: <id> | model не є опублікованим id. На /v1/responses також коли model відсутнє. |
400 | invalid_request_error | No user message provided | Рівні Shannon: у запиті немає тексту користувача. |
400 | invalid_request_error | <id> does not accept image input | Зображення надіслано до хостованої open-weight моделі без підтримки вхідних зображень. |
400 | invalid_request_error | audio and video input are not supported by any hosted model | Аудіо- або відеочастину надіслано до хостованої open-weight моделі. |
400 | invalid_request_error | <id> does not accept response_format | Структурований вивід запитано в хостованої open-weight моделі, що його не має. |
400 | invalid_request_error | <id> does not accept response_format json_schema; use json_object | JSON-схему надіслано до хостованої open-weight моделі, що приймає лише json_object. |
400 | invalid_request_error | unknown reasoning effort '<value>'; expected off, low, medium or high | Зусилля міркування не є одним із перелічених значень. Хостовані open-weight моделі. |
400 | invalid_request_error | count_tokens is available for the hosted open models; unknown model: <id> | Підрахунок токенів запитано для моделі поза хостованими open-weight моделями. |
401 | authentication_error | Missing authentication | API-ключ не надіслано. |
401 | authentication_error | Invalid API key | Ключ невідомий, був замінений новішим або був відкликаний. |
404 | not_found_error | Not Found | Шляху не існує, наприклад /v1/embeddings. |
405 | api_error | Method Not Allowed | Шлях існує, але не з цим методом HTTP. |
413 | invalid_request_error | Failed to buffer the request body: … | Тіло запиту більше за 32 MiB. |
415 | invalid_request_error | Expected request with `Content-Type: application/json` | Заголовок Content-Type відсутній або не дорівнює application/json. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | Обов'язкове поле відсутнє, наприклад messages, або поле має неправильний тип JSON. |
429 | rate_limit_error | Quota exceeded. Upgrade your plan at shannon-ai.com/plan | Ліміт виводу запиту більший за залишок вашого балансу. |
429 | rate_limit_error | Too many requests. Retry in <n>s. | Захист від флуду: понад 120 запитів за одну хвилину з вашого облікового запису. <n> — кількість секунд до кінця хвилини. |
429 | rate_limit_error | Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan | shannon-coder-1 на /v1/chat/completions і /v1/messages: виклики Shannon Coder за вашим планом у поточному 4-годинному вікні вичерпано. |
429 | rate_limit_error | Shannon routes are temporarily busy. Please retry. | Модель у цей момент зайнята. |
500 | server_error | The model backend failed to answer. Please retry. | Модель не сформувала відповіді. |
500 | server_error | An internal error occurred. Please retry. | Внутрішній збій під час підготовки запиту. |
502 | api_error | The model backend failed to answer. Please retry. | Модель не сформувала відповіді. Сімейство Shannon 3 і хостовані open-weight моделі. |
503 | api_error | Could not verify your quota right now. Please retry. | Ваш баланс зараз не вдалося прочитати. |
503 | api_error | token counting is temporarily unavailable for this model | Підрахунок токенів для цієї моделі зараз недоступний. |
504 | api_error | upstream timed out | Хостована open-weight модель відповідала довше за обмеження часу для запиту без стрімінгу. |
Перевірки виконуються в такому порядку: API-ключ, тіло запиту, id моделі, захист від флуду, баланс. Тому запит із неправильним ключем і неправильним тілом повертає 401. Запит, відхилений зі статусом 4xx, не тарифікується.
Баланс і захист від флуду
Кожен запит обслуговується однаково: немає тарифних рівнів швидкості й окремої квоти API. Проте 429 можуть дати дві речі, обидві з типом rate_limit_error. Їх розрізняє повідомлення.
Баланс
Ваш баланс — це сьогоднішній ліміт плану, що оновлюється о 00:00 UTC, а після нього придбані кредити. Чат і API беруть із того самого балансу.
Кожен запит спершу резервує свій ліміт виводу: max_tokens або max_output_tokens на /v1/responses, 4,096, якщо ви не надсилаєте жодного. Коли відповідь завершено, зарезервована сума замінюється тим, що використав запит. Якщо сума не вміщується в залишок балансу, запит повертає Quota exceeded до того, як модель почне працювати.
Запити, що виконуються одночасно, резервують кожен свою суму. Тому багато паралельних запитів із великим max_tokens можуть на мить вичерпати баланс, навіть якщо кожна відповідь коротка. Менше max_tokens резервує менше.
Захист від флуду
Захист від флуду дозволяє 120 запитів на хвилину на обліковий запис. Він рахує обліковий запис, а не ключ, по /v1/chat/completions, /v1/responses і /v1/messages разом. Хвилина починається з вашого першого запиту.
Запит номер 121 у цю хвилину повертає Too many requests. Retry in <n>s. і нічого не коштує. Відхилені запити теж рахуються, тож клієнт, який надсилає знову без очікування, лишається відхиленим до кінця хвилини. Зачекайте <n> секунд із повідомлення.
Запити, надіслані паралельно, не відхиляються за паралельність. Вони чекають у черзі й обслуговуються по черзі.
GET /v1/models та ендпоінти підрахунку токенів не рахуються.
Більше про ліміти й баланс і де побачити власний баланс та використання: Ліміти та баланс Ключі та використання
Що повторювати
Надсилайте запит знову лише тоді, коли той самий запит може вдатися. SDK від OpenAI та Anthropic уже самі двічі повторюють відповіді 429 і 5xx; змініть це через max_retries (maxRetries у JavaScript).
| Помилка | Надсилати знову? | Як |
|---|---|---|
400, 404, 405, 413, 415, 422 | Ні. Спершу виправте запит. | Повідомлення називає поле, модель або вхід, що неправильні. |
401 | Ні. Використайте чинний ключ. | Створіть або замініть ключ на сторінці «Ключі та використання». |
429 Too many requests | Так, після очікування. | Зачекайте кількість секунд із повідомлення, потім надішліть той самий запит. |
429 Quota exceeded | Не в такому вигляді. | Зменште max_tokens, зачекайте на щоденне оновлення о 00:00 UTC або додайте кредит. |
429 Shannon Coder call quota reached | Так, у наступному вікні. | Вікна починаються кожні 4 години від 00:00 UTC. Інша модель відповідає одразу. |
429 … temporarily busy | Так. | Зачекайте кілька секунд, а потім довше при кожній наступній спробі. |
500, 502, 503 | Так. | Зачекайте 1 секунду, потім 2, 4 і 8. Зупиніться після кількох спроб і повідомте id запиту. |
504 | Так. | Надішліть із stream, встановленим у true: обмеження часу діє для відповідей без стрімінгу. |
Цикл повторів за цією таблицею:
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); Помилки всередині стріму
Стрім має статус 200 від першого байта. Збій після цього надходить як подія всередині стріму: фрагмент із членом error на /v1/chat/completions, подія error на /v1/messages, подія response.failed або error на /v1/responses. Завершальні події стріму все одно йдуть далі. Вважайте таку відповідь невдалою та надішліть запит знову, як за статусу 5xx.
Розмова, довша за вікно контексту моделі, повідомляється саме так, з типом invalid_request_error і кодом context_length_exceeded. Скоротіть розмову, перш ніж надсилати знову.
Точні кадри описано разом зі стрімінгом. Стримінг
Id запитів
Кожна відповідь, включно з помилками, має заголовок x-request-id. Це значення, яке ви надіслали у власному заголовку x-request-id, або 12 шістнадцяткових символів, якщо ви нічого не надсилали. Логуйте його та наводьте, коли просите допомоги із запитом.