مدیریت خطا
درخواستی که ناموفق باشد یک کد وضعیت 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 پاسخ میدهد؛ هر endpoint دیگر با فرمت OpenAI. در هر دو، جزئیات در error.type و error.message است، پس یک خواننده برای همه endpointها کافی است.
{
"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 | خود درخواست نادرست است: یک فیلد، بدنه، شناسه مدل یا ورودیای که مدل نمیپذیرد. پیش از ارسال دوباره درخواست را تغییر دهید. |
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 شناسه منتشرشده نیست. روی /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 schema به مدل 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. | محافظت در برابر هجوم درخواست (flood protection): بیش از 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 میزبانیشده بیش از محدودیت زمانیِ درخواستی که stream نمیشود طول کشید. |
بررسیها به این ترتیب انجام میشوند: کلید API، بدنه درخواست، شناسه مدل، محافظت در برابر هجوم درخواست، موجودی. بنابراین درخواستی با کلید نادرست و بدنه نادرست 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 پایینتر مقدار کمتری کنار میگذارد.
محافظت در برابر هجوم درخواست
محافظت در برابر هجوم درخواست (flood protection) 120 درخواست در دقیقه برای هر حساب مجاز میداند. حساب را میشمارد، نه کلید را، و /v1/chat/completions، /v1/responses و /v1/messages را با هم در نظر میگیرد. دقیقه با اولین درخواست شما شروع میشود.
درخواست شماره 121 در آن دقیقه Too many requests. Retry in <n>s. برمیگرداند و هزینهای ندارد. درخواستهای ردشده هم شمرده میشوند، پس کلاینتی که بدون انتظار دوباره میفرستد تا پایان دقیقه رد میشود. به اندازه <n> ثانیهای که در پیام آمده صبر کنید.
درخواستهایی که موازی فرستاده میشوند بهخاطر موازی بودن رد نمیشوند. در صف منتظر میمانند و به نوبت پاسخ داده میشوند.
GET /v1/models و endpointهای شمارش توکن شمرده نمیشوند.
بیشتر درباره محدودیتها و موجودی، و اینکه موجودی و مصرف خود را کجا ببینید: محدودیتها و موجودی کلیدها و مصرف
چه چیزی را دوباره امتحان کنیم
فقط وقتی درخواست را دوباره بفرستید که همان درخواست بتواند موفق شود. SDKهای OpenAI و Anthropic پاسخهای 429 و 5xx را خودشان دو بار دوباره امتحان میکنند؛ برای تغییر آن max_retries (در JavaScript maxRetries) را تنظیم کنید.
| خطا | دوباره بفرستم؟ | چگونه |
|---|---|---|
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. پس از چند تلاش متوقف شوید و شناسه درخواست را گزارش کنید. |
504 | بله. | آن را با stream برابر true بفرستید: محدودیت زمانی برای پاسخهایی اعمال میشود که stream نمیشوند. |
یک حلقه تلاش دوباره که از این جدول پیروی میکند:
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); خطاها داخل یک stream
stream از اولین بایت وضعیت 200 دارد. شکست پس از آن بهصورت یک رویداد داخل stream میرسد: یک chunk با عضو error روی /v1/chat/completions، یک رویداد error روی /v1/messages، یک رویداد response.failed یا error روی /v1/responses. رویدادهای پایانی stream همچنان بعد از آن میآیند. چنین پاسخی را ناموفق در نظر بگیرید و درخواست را دوباره بفرستید، مانند وضعیت 5xx.
گفتگویی که از پنجره زمینه مدل بلندتر باشد به همین شکل، با نوع invalid_request_error و کد context_length_exceeded، گزارش میشود. پیش از ارسال دوباره، گفتگو را کوتاه کنید.
فریمهای دقیق همراه با streaming توضیح داده شدهاند. استریم
شناسههای درخواست
هر پاسخ، از جمله خطاها، هدر x-request-id دارد. مقداری است که در هدر x-request-id خودتان فرستادهاید، یا 12 نویسه هگزادسیمال اگر چیزی نفرستاده باشید. آن را لاگ کنید و هنگام درخواست کمک درباره یک درخواست ذکر کنید.