uttapen

خطاها و کدها — ۴۰۰ تا ۵۰۴ و راه‌حل هر کدام

جدول کامل خطاهای 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 روی خطاها هم هست. برای گزارش مشکل همین را بفرست.

جدول خطاها

statuscodeکِیچه کنی
400invalid_requestJSON نامعتبر، model نبود، فیلد عددی رشته یا اعشاری بود، مدل embeddings نمی‌دهد، plugins یا models شکل غلط داردبدنه را درست کن؛ تکرار بی‌فایده است
400invalid_request (از upstream)پارامتری که مدل نمی‌پذیرد (مثلاً temperature روی مدل استدلالی، تصویر به مدل متنی) یا درخواست بزرگ‌تر از ظرفیت لحظه‌ای providerپارامتر را حذف کن یا max_tokens را کم کن
401invalid_api_keyکلید نیامده، غلط، لغوشده یا منقضیکلید تازه از داشبورد؛ تکرار نکن
402insufficient_balanceموجودی قابل‌استفاده کمتر از مبلغ رزرو این درخواست، یا موجودی منفیشارژ کن (uttapen.topup_url) یا max_tokens را کم کن؛ درخواست به provider نرفته
403account_suspendedحساب توسط ادمین معلق شدهبا پشتیبانی تماس بگیر
403model_not_allowedمدل بیرون از allowed_models این کلیدمدل دیگر یا تنظیم کلید
404model_not_foundشناسهٔ مدل وجود ندارد، مخفی یا حذف شده؛ شناسه بدون providerشناسه را از /v1/models بردار
404not_foundمسیر وجود ندارد یا /v1/responses که هنوز فعال نیست/v1/chat/completions
413request_too_largeبدنه بزرگ‌تر از ۲۰ مگابایتتصویر را کوچک کن یا متن PDF را بفرست
429rate_limit_exceededبیش از rate_limit_rpm کلید در یک دقیقهRetry-After را صبر کن
429key_monthly_limit_reachedسقف ماهانهٔ کلید پر شده (uttapen.monthly_limit_toman، spent_toman)سقف را بالا ببر یا کلید دیگر
429too_many_concurrent_streamsبیش از ۱۰ استریم باز برای این کاربریکی تمام شود؛ Retry-After: 5
429free_quota_exceededسهم روزانهٔ مدل‌های رایگان تمام شد (uttapen.alternative_model)مدل پولی یا فردا
429free_capacity_exhaustedظرفیت مشترک مدل رایگان در provider تمام شدهمدل پولی؛ Retry-After
429upstream_rate_limitedprovider این مدل را موقتاً محدود کردهچند ثانیه صبر و تکرار، یا مدل هم‌رده
502upstream_errorخطای ۵xx provider، پاسخ ناقص، یا خطای میان‌استریمتکرار با backoff؛ رزرو آزاد یا با مصرف واقعی تسویه شده
503upstream_unavailableاتصال به provider برقرار نشد یا هیچ ظرفیتی در دسترس نیستRetry-After: 30؛ تکرار
503pricing_unavailableقیمت‌گذاری موقتاً تنظیم نشده (فقط در راه‌اندازی اولیه)چند دقیقه بعد
504upstream_timeout۱۲۰ ثانیه بدون هیچ بایتی از providerتکرار؛ برای مدل‌های کند stream: true
4xx دیگرهمان کد providerخطای provider با همان statusپیام را بخوان

خطاهای 5xx و 429 امن برای تکرارند: هیچ‌کدام بدون این که پاسخ بگیری هزینه‌ای برایت نمی‌گذارند. 400 و 401 و 403 و 404 را تکرار نکن؛ نتیجه همان است.

هزینه در خطا

قاعده ساده است: اگر پاسخ نگرفتی، پول نداده‌ای. رزرو در خطاهای قبل از ارسال اصلاً انجام نمی‌شود و در خطاهای provider همان لحظه آزاد می‌شود. تنها استثنا خطای وسط استریم است که بخشی از پاسخ رسیده؛ آن‌جا فقط مصرف واقعی تا لحظهٔ قطع تسویه می‌شود (استریم).

نگاشت به SDKها

Python (openai) — هر status به یک کلاس:

statusexception
400openai.BadRequestError
401openai.AuthenticationError
402openai.APIStatusError (کلاس اختصاصی ندارد؛ status_code == 402)
403openai.PermissionDeniedError
404openai.NotFoundError
413openai.APIStatusError
429openai.RateLimitError
502، 503، 504openai.InternalServerError
قطع شبکه / timeoutopenai.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 و 5xx backoff نمایی با jitter و حداکثر سه تلاش. Retry-After را اگر بود، رعایت کن.
  • 402 را به کاربر یا ادمین خودت نشان بده و تکرار نکن؛ تا شارژ نشود همان می‌ماند.
  • X-Uttapen-Request-Id را در لاگ خودت کنار خطا بنویس. وقتی از پشتیبانی می‌پرسی، با همین شناسه در چند ثانیه پیدایش می‌کنیم.
  • قبل از deploy، GET /v1/uttapen/me را بزن تا کلید، مدل مجاز و موجودی را یک‌جا ببینی.