خطاها و کدها — ۴۰۰ تا ۵۰۴ و راهحل هر کدام
جدول کامل خطاهای API از 400 تا 504 با کد هر خطا، شکل JSON خطا و بلوک یوتاپن، کار درست در هر حالت، و نگاشت خطاها به exception های SDK رسمی OpenAI.
بهروزرسانی: ۱۶ شهریور ۱۴۰۵
همهٔ خطاها با همان شکل خطای OpenAI برمیگردند تا SDKها آنها را به exception درست تبدیل کنند. فرق ما یک بلوک اضافه به نام uttapen است که در بعضی خطاها اطلاعات عملی میدهد (مثلاً مبلغ لازم و لینک شارژ).
شکل خطا
{
"error": {
"message": "موجودی کافی نیست. برای این درخواست حدود 151٬656 تومان لازم است و موجودی قابلاستفاده 97٬518 تومان است.",
"type": "insufficient_balance",
"code": "insufficient_balance",
"param": null,
"uttapen": {
"balance_toman": "97517.828964",
"available_toman": "97517.828964",
"required_toman": "151656.439570",
"topup_url": "https://uttapen.ir/dashboard/wallet"
}
}
}
codeوtypeهمیشه یکیاند؛ رویcodeشرط بگذار.messageفارسی و برای نمایش به کاربر نهایی مناسب است. پیامهایی که از provider میآیند انگلیسیاند.uttapenفقط وقتی هست که چیزی برای گفتن داریم؛ روی وجودش شرط بگذار.- هدر
X-Uttapen-Request-Idروی خطاها هم هست. برای گزارش مشکل همین را بفرست.
جدول خطاها
| status | code | کِی | چه کنی |
|---|---|---|---|
400 | invalid_request | JSON نامعتبر، model نبود، فیلد عددی رشته یا اعشاری بود، مدل embeddings نمیدهد، plugins یا models شکل غلط دارد | بدنه را درست کن؛ تکرار بیفایده است |
400 | invalid_request (از upstream) | پارامتری که مدل نمیپذیرد (مثلاً temperature روی مدل استدلالی، تصویر به مدل متنی) یا درخواست بزرگتر از ظرفیت لحظهای provider | پارامتر را حذف کن یا max_tokens را کم کن |
401 | invalid_api_key | کلید نیامده، غلط، لغوشده یا منقضی | کلید تازه از داشبورد؛ تکرار نکن |
402 | insufficient_balance | موجودی قابلاستفاده کمتر از مبلغ رزرو این درخواست، یا موجودی منفی | شارژ کن (uttapen.topup_url) یا max_tokens را کم کن؛ درخواست به provider نرفته |
403 | account_suspended | حساب توسط ادمین معلق شده | با پشتیبانی تماس بگیر |
403 | model_not_allowed | مدل بیرون از allowed_models این کلید | مدل دیگر یا تنظیم کلید |
404 | model_not_found | شناسهٔ مدل وجود ندارد، مخفی یا حذف شده؛ شناسه بدون provider | شناسه را از /v1/models بردار |
404 | not_found | مسیر وجود ندارد یا /v1/responses که هنوز فعال نیست | /v1/chat/completions |
413 | request_too_large | بدنه بزرگتر از ۲۰ مگابایت | تصویر را کوچک کن یا متن PDF را بفرست |
429 | rate_limit_exceeded | بیش از rate_limit_rpm کلید در یک دقیقه | Retry-After را صبر کن |
429 | key_monthly_limit_reached | سقف ماهانهٔ کلید پر شده (uttapen.monthly_limit_toman، spent_toman) | سقف را بالا ببر یا کلید دیگر |
429 | too_many_concurrent_streams | بیش از ۱۰ استریم باز برای این کاربر | یکی تمام شود؛ Retry-After: 5 |
429 | free_quota_exceeded | سهم روزانهٔ مدلهای رایگان تمام شد (uttapen.alternative_model) | مدل پولی یا فردا |
429 | free_capacity_exhausted | ظرفیت مشترک مدل رایگان در provider تمام شده | مدل پولی؛ Retry-After |
429 | upstream_rate_limited | provider این مدل را موقتاً محدود کرده | چند ثانیه صبر و تکرار، یا مدل همرده |
502 | upstream_error | خطای ۵xx provider، پاسخ ناقص، یا خطای میاناستریم | تکرار با backoff؛ رزرو آزاد یا با مصرف واقعی تسویه شده |
503 | upstream_unavailable | اتصال به provider برقرار نشد یا هیچ ظرفیتی در دسترس نیست | Retry-After: 30؛ تکرار |
503 | pricing_unavailable | قیمتگذاری موقتاً تنظیم نشده (فقط در راهاندازی اولیه) | چند دقیقه بعد |
504 | upstream_timeout | ۱۲۰ ثانیه بدون هیچ بایتی از provider | تکرار؛ برای مدلهای کند stream: true |
4xx دیگر | همان کد provider | خطای provider با همان status | پیام را بخوان |
خطاهای 5xx و 429 امن برای تکرارند: هیچکدام بدون این که پاسخ بگیری هزینهای برایت نمیگذارند. 400 و 401 و 403 و 404 را تکرار نکن؛ نتیجه همان است.
هزینه در خطا
قاعده ساده است: اگر پاسخ نگرفتی، پول ندادهای. رزرو در خطاهای قبل از ارسال اصلاً انجام نمیشود و در خطاهای provider همان لحظه آزاد میشود. تنها استثنا خطای وسط استریم است که بخشی از پاسخ رسیده؛ آنجا فقط مصرف واقعی تا لحظهٔ قطع تسویه میشود (استریم).
نگاشت به SDKها
Python (openai) — هر status به یک کلاس:
| status | exception |
|---|---|
400 | openai.BadRequestError |
401 | openai.AuthenticationError |
402 | openai.APIStatusError (کلاس اختصاصی ندارد؛ status_code == 402) |
403 | openai.PermissionDeniedError |
404 | openai.NotFoundError |
413 | openai.APIStatusError |
429 | openai.RateLimitError |
502، 503، 504 | openai.InternalServerError |
| قطع شبکه / timeout | openai.APIConnectionError، openai.APITimeoutError |
import openai
try:
resp = client.chat.completions.create(model="openai/gpt-5-mini", messages=msgs)
except openai.AuthenticationError:
raise SystemExit("کلید uttapen نامعتبر است")
except openai.RateLimitError as e:
wait = int(e.response.headers.get("retry-after", "5"))
time.sleep(wait)
except openai.APIStatusError as e:
if e.status_code == 402:
info = e.body["error"]["uttapen"]
print("موجودی کافی نیست؛ لازم:", info["required_toman"], "تومان —", info["topup_url"])
else:
print(e.status_code, e.code, e.message)
e.code همان error.code ماست، e.body کل بدنهٔ JSON و e.response.headers هدرها (شامل x-uttapen-request-id). SDK بهصورت پیشفرض 429 و 5xx را دو بار با backoff تکرار میکند؛ با max_retries تنظیمش کن.
Node.js (openai) — همان نامها: AuthenticationError، RateLimitError، NotFoundError، BadRequestError، PermissionDeniedError، InternalServerError؛ برای 402 کلاس عمومی APIError با err.status === 402. err.code و err.error.uttapen در دسترساند.
try {
await client.chat.completions.create({ model: "openai/gpt-5-mini", messages });
} catch (err) {
if (err instanceof OpenAI.APIError) {
console.error(err.status, err.code, err.error?.uttapen);
} else throw err;
}
PHP (openai-php/client) — همهٔ خطاهای HTTP OpenAI\Exceptions\ErrorException هستند با getStatusCode()، getErrorCode() و getMessage(). بلوک uttapen را این کلاینت نگه نمیدارد؛ برای 402 مبلغ را از پیام بخوان یا با Guzzle خام بدنه را بگیر.
Go (openai-go) — var apierr *openai.Error; errors.As(err, &apierr) سپس apierr.StatusCode، apierr.Code، apierr.Message و apierr.RawJSON() برای بلوک uttapen.
توصیه برای کد تولیدی
- روی
codeشرط بگذار نه روی متنmessage؛ متن ممکن است تغییر کند. - برای
429و5xxbackoff نمایی با jitter و حداکثر سه تلاش.Retry-Afterرا اگر بود، رعایت کن. 402را به کاربر یا ادمین خودت نشان بده و تکرار نکن؛ تا شارژ نشود همان میماند.X-Uttapen-Request-Idرا در لاگ خودت کنار خطا بنویس. وقتی از پشتیبانی میپرسی، با همین شناسه در چند ثانیه پیدایش میکنیم.- قبل از deploy،
GET /v1/uttapen/meرا بزن تا کلید، مدل مجاز و موجودی را یکجا ببینی.