۱۲ خطای رایج API هوش مصنوعی و راهحل دقیق هرکدام
ساعت سه بامداد است و لاگ سرور پر شده از یک خط تکراری: Error code: 429. ربات پشتیبانی جواب نمیدهد، صف پیامها بالا رفته و شما نمیدانید مشکل از موجودی حساب است، از سقف درخواست در دقیقه، یا از کلیدی که دیروز عوض کردید. مشکل اصلی این است که کد وضعیت HTTP تقریباً هیچچیز به شما نمیگوید؛ چیزی که واقعاً مشکل را مشخص میکند، فیلد code داخل بدنهٔ JSON پاسخ است. در این مقاله دوازده خطای پرتکرار APIهای سازگار با OpenAI را با بدنهٔ واقعی خطا، علت دقیق و راهحل عملی مرور میکنیم و در پایان یک الگوی تلاش مجدد میدهیم که در محیط واقعی جواب میدهد.
چرا باید بدنهٔ خطا را بخوانید، نه فقط کد وضعیت را؟
در همهٔ درگاههای سازگار با OpenAI، هر پاسخ ناموفق یک بدنهٔ JSON با ساختار ثابت برمیگرداند. این ساختار چهار فیلد دارد و مهمترینشان آخری است:
{
"error": {
"message": "متن قابل خواندن برای انسان",
"type": "دستهٔ کلی خطا، مثل invalid_request_error",
"param": "نام پارامتر مقصر، اگر وجود داشته باشد",
"code": "شناسهٔ ماشینخوان خطا، مثل insufficient_quota"
}
}
اشتباه رایج این است که برنامه فقط status_code را نگاه میکند. اما ۴۲۹ دو معنی کاملاً متفاوت دارد: یکی «سرعتت زیاد است، چند صد میلیثانیه صبر کن» و دیگری «اعتبارت تمام شده، هرچقدر هم صبر کنی درست نمیشود». اگر روی هر دو یکسان تلاش مجدد کنید، در حالت دوم بیدلیل سرور خودتان را قفل میکنید. پس اولین قانون: همیشه error.code را لاگ کنید و تصمیمها را بر اساس آن بگیرید.
خطاهای احراز هویت: ۴۰۱ و ۴۰۳
۱. invalid_api_key — شایعترین خطای روز اول. بدنهٔ پاسخ چیزی شبیه این است:
{
"error": {
"message": "Incorrect API key provided: sk-ntz-***. You can find your API key in your dashboard.",
"type": "invalid_request_error",
"param": null,
"code": "invalid_api_key"
}
}
علت واقعی معمولاً یکی از این چهار مورد است: کلید هنگام کپی یک کاراکتر کم یا زیاد شده، فایل .env در سرور بهروز نشده، متغیر محیطی در فرایند اجرا لود نشده، یا کلید را برای یک درگاه ساختهاید و روی درگاه دیگری استفاده میکنید. کلیدهای نِت اَرز با پیشوند sk-ntz-v1- شروع میشوند و فقط با آدرس پایهٔ https://netarz.ir/api/ai/v1 کار میکنند؛ اگر کلید نِت اَرز را روی آدرس رسمی OpenAI بفرستید دقیقاً همین خطا را میگیرید.
۲. نبودن هدر Authorization — پیام «You didn't provide an API key» یعنی هدر اصلاً ارسال نشده است. در curl معمولاً بهخاطر جاافتادن Bearer اتفاق میافتد و در Node به این دلیل که process.env.NETARZ_AI_KEY مقدار undefined داشته و رشتهٔ «Bearer undefined» ساخته شده است. یک بار مقدار کلید را قبل از ساختن کلاینت لاگ کنید (فقط چند کاراکتر اولش) تا خیالتان راحت شود.
۳. unsupported_country_region_territory — این همان ۴۰۳ معروف است:
{
"error": {
"message": "Country, region, or territory not supported",
"type": "request_forbidden",
"param": null,
"code": "unsupported_country_region_territory"
}
}
این خطا ربطی به کلید شما ندارد؛ ارائهدهنده IP مبدأ را دیده و درخواست را رد کرده است. برای کسی که مستقیم به سرویس اصلی وصل میشود، تنها راه واقعی این است که کد روی سروری خارج از محدودهٔ مسدود اجرا شود. اگر از یک درگاه مثل API هوش مصنوعی نِت اَرز استفاده میکنید، درخواست از سمت سرور درگاه ارسال میشود و این خطا اصلاً به شما نمیرسد. توضیح کامل ماجرا را در راهنمای API هوش مصنوعی با پرداخت ریالی نوشتهایم.
خطاهای سهمیه و اعتبار: دو ۴۲۹ که هیچ شباهتی به هم ندارند
۴. rate_limit_exceeded — یعنی از سقف درخواست در دقیقه (RPM) یا توکن در دقیقه (TPM) عبور کردهاید. پیام معمولاً خودش زمان انتظار را میگوید:
{
"error": {
"message": "Rate limit reached for gpt-5-mini in organization org-xxxx on requests per min (RPM): Limit 500, Used 500, Requested 1. Please try again in 120ms.",
"type": "requests",
"param": null,
"code": "rate_limit_exceeded"
}
}
این خطا موقتی است و دقیقاً همان جایی است که تلاش مجدد معنا دارد. اگر هدر retry-after در پاسخ بود، به همان اندازه صبر کنید؛ اگر نبود، از تأخیر نمایی استفاده کنید. راهحل ساختاری هم این است که بهجای فرستادن صد درخواست همزمان، یک صف با محدودیت همزمانی بگذارید.
۵. insufficient_quota — همان ۴۲۹ است اما معنیاش کاملاً فرق دارد:
{
"error": {
"message": "You exceeded your current quota, please check your plan and billing details.",
"type": "insufficient_quota",
"param": null,
"code": "insufficient_quota"
}
}
یعنی اعتبار حساب تمام شده یا صفر بوده است. تلاش مجدد اینجا نهتنها بیفایده است، بلکه لاگ شما را پر میکند و ممکن است باعث محدود شدن موقت حسابتان شود. این خطا باید بلافاصله باعث توقف حلقه و ارسال هشدار به تیم شود. اگر روی درگاه نِت اَرز کار میکنید، موجودی دلاری حساب و مصرف هر پروژه در پنل قابل مشاهده است و بهتر است پیش از رسیدن به صفر هشدار موجودی را فعال کنید. برای درک بهتر اینکه اعتبار چطور مصرف میشود، راهنمای محاسبهٔ توکن و هزینه را ببینید.
خطاهای ورودی: وقتی درخواست شما ایراد دارد
۶. context_length_exceeded — ترسناکترین خطا برای کسی که تاریخچهٔ گفتگو را بیحساب نگه میدارد:
{
"error": {
"message": "This model's maximum context length is 128000 tokens. However, your messages resulted in 131204 tokens. Please reduce the length of the messages.",
"type": "invalid_request_error",
"param": "messages",
"code": "context_length_exceeded"
}
}
نکتهٔ مهم برای ما: متن فارسی به ازای هر کلمه توکن بیشتری از انگلیسی مصرف میکند، پس یک گفتگوی فارسی خیلی زودتر از آنچه فکر میکنید به سقف میرسد. سه راهحل عملی دارید: تاریخچه را به n پیام آخر محدود کنید، پیامهای قدیمی را با یک فراخوانی جداگانه خلاصه کنید و خلاصه را جایگزین کنید، یا اگر متن مرجع بزرگی دارید اصلاً همهاش را نفرستید و فقط تکههای مرتبط را بازیابی کنید. روش سوم همان چیزی است که در راهنمای ساخت چتبات RAG روی اسناد فارسی قدمبهقدم توضیح دادهایم.
۷. model_not_found — نام مدل را اشتباه نوشتهاید یا آن مدل روی حساب شما فعال نیست:
{
"error": {
"message": "The model `gpt-5-turbo` does not exist or you do not have access to it.",
"type": "invalid_request_error",
"param": "model",
"code": "model_not_found"
}
}
پیش از هر چیز فهرست مدلهای واقعاً فعال را از خود درگاه بگیرید، نه از حافظه. روی درگاه نِت اَرز مدلهای OpenAI، Google و DeepSeek مسیر مستقیم دارند و بقیه (از جمله Claude، Grok، Mistral، Llama و Sonar) از مسیر OpenRouter و با شناسهای که با openrouter/ شروع میشود فراخوانی میشوند. شناسهها هم بازنشسته میشوند، پس نام مدل را از متغیر محیطی بخوانید تا عوض کردنش یک خط تغییر باشد؛ فهرست بهروز و نرخ هر مدل در صفحهٔ مدلها منتشر میشود. با یک درخواست ساده هم میتوانید فهرست را بگیرید: GET /api/ai/v1/models.
۸. invalid_request_error روی پارامترها — فیلد param اینجا دقیقاً به شما میگوید کجا را نگاه کنید. رایجترین نمونهها: فرستادن messages بهصورت رشته بهجای آرایه، جا انداختن کلید role، مقدار نامعتبر برای temperature، یا فرستادن پارامتری که آن مدل خاص پشتیبانی نمیکند. اگر پیام گفت پارامتری پشتیبانی نمیشود، آن را حذف کنید؛ نگه داشتنش هیچ سودی ندارد.
۹. خطای طول خروجی — این یکی اصلاً خطای HTTP نیست و برای همین از همه خطرناکتر است. پاسخ با کد ۲۰۰ برمیگردد ولی finish_reason برابر length است؛ یعنی مدل وسط جمله قطع شده. اگر خروجی JSON میخواستید، همانجا هنگام JSON.parse برنامهتان میشکند. همیشه finish_reason را چک کنید.
خطاهای سمت سرور و شبکه
۱۰. server_error (۵۰۰) — ایراد از سمت ارائهدهنده است. یک یا دو بار تلاش مجدد کنید؛ اگر ادامه داشت، صفحهٔ وضعیت سرویس را ببینید.
۱۱. سرویس در دسترس نیست (۵۰۳ یا ۵۲۹) — موتور در اوج مصرف است. تلاش مجدد با تأخیر نمایی درستترین کار است و اگر برنامهٔ شما حساس است، یک مدل جایگزین بهعنوان مسیر پشتیبان تعریف کنید.
۱۲. تایماوت و قطع اتصال — در Python با APITimeoutError و APIConnectionError و در Node با خطای fetch failed خودش را نشان میدهد. برای درخواستهای سنگین تایماوت پیشفرض کتابخانه معمولاً کم است؛ آن را دستی بالا ببرید و اگر خروجی طولانی میخواهید از حالت استریم استفاده کنید تا اتصال زنده بماند.
پیامهای خطای فارسی وبسرویس نِت اَرز یعنی چه؟
اگر کلیدتان از وبسرویس هوش مصنوعی نِت اَرز است، چند خطا با متن فارسی برمیگردند. هیچکدام خرابی سرویس نیست: هر کدام یعنی یکی از سقفهای حساب، پروژه یا کلید پر شده است. متن فیلد message ممکن است کمی عوض شود، پس برنامهتان را روی فیلد code بنویسید. هر پنج پیام را با همان متن پایین آوردهایم.
«بودجهٔ ماهانهٔ این پروژه تمام شده است.» یعنی چه؟
یعنی هزینهٔ این ماه پروژه به بودجهٔ ماهانهای رسیده که خودتان در تنظیمات پروژه گذاشتهاید؛ اعتبار حسابتان لزوماً تمام نشده است. کد خطا project_budget_exceeded با وضعیت ۴۰۲ است. در صفحهٔ پروژه (/ai/projects) بودجه را بالا ببرید یا خانهاش را خالی بگذارید تا سقف برداشته شود. وگرنه شمارش از اول ماه میلادی بعد از نو شروع میشود.
پیش از هر درخواست، هزینهٔ احتمالیاش را از اعتبار کنار میگذاریم. برای همین این پیام ممکن است کمی پیش از پر شدن کامل بودجه بیاید؛ با max_tokens کوچکتر، این مبلغ هم کوچکتر است.
«سقف مصرف تعیینشده برای این کلید API پر شده است.» یعنی چه؟
یعنی مصرف همین کلید، از روز ساخت تا امروز، به سقف هزینهای رسیده که برایش گذاشتهاید. کد خطا key_spend_limit_reached با وضعیت ۴۰۲ است و کلیدهای دیگر حساب کار میکنند. در صفحهٔ پروژه، دکمهٔ «سقف هزینه» کنار همان کلید را بزنید و سقف را بالا ببرید یا خالی بگذارید. کلید تازه لازم نیست.
«سقف مصرف روزانهٔ حساب شما پر شده است.» یعنی چه؟
یعنی جمع هزینهٔ امروز همهٔ پروژهها و کلیدهای حسابتان به سقف روزانهٔ حساب رسیده است. کد خطا daily_cap_reached با وضعیت ۴۰۲ است. روز این سقف با ساعت جهانی (UTC) حساب میشود، پس شمارش ساعت ۳:۳۰ بامداد به وقت تهران از صفر شروع میشود. اگر برای کارتان سقف بالاتری لازم دارید، تیکت بفرستید.
«سقف استفادهٔ آزمایشی بدون احراز هویت ($2.00) پر شده است.» یعنی چه؟
یعنی حسابتان ۲ دلار از وبسرویس مصرف کرده و حالا یک بار احراز هویت لازم است. کد خطا kyc_required با وضعیت ۴۰۳ است. در صفحهٔ احراز هویت اول شمارهٔ موبایل را با کد پیامکی تأیید کنید، بعد مدارک را بفرستید. اعتبار باقیمانده سر جایش میماند و بعد از تأیید، با همان کلید ادامه میدهید.
«مدارک احراز هویت شما در حال بررسی است» یعنی چه؟
این پیام وقتی میآید که سقف ۲ دلاری پر شده ولی مدارکتان را فرستادهاید. کار دیگری لازم نیست: بعد از تأیید، محدودیت خودش برداشته میشود و با همان کلید و همان اعتبار ادامه میدهید. لازم هم نیست دوباره شارژ کنید. وضعیت بررسی را در صفحهٔ احراز هویت میبینید.
جدول جمعبندی دوازده خطا
| کد وضعیت | error.code | علت واقعی | تلاش مجدد؟ | کار درست |
|---|---|---|---|---|
| ۴۰۱ | invalid_api_key | کلید غلط یا مربوط به درگاه دیگر | خیر | کلید و آدرس پایه را با هم چک کنید |
| ۴۰۱ | بدون کلید | هدر Authorization ارسال نشده | خیر | لود شدن متغیر محیطی را بررسی کنید |
| ۴۰۳ | unsupported_country_region_territory | IP مبدأ پذیرفته نشده | خیر | اجرا روی سرور مناسب یا استفاده از درگاه API |
| ۴۰۴ | model_not_found | نام مدل غلط یا غیرفعال | خیر | فهرست مدلها را از خود درگاه بگیرید |
| ۴۲۹ | rate_limit_exceeded | عبور از سقف RPM یا TPM | بله | احترام به retry-after و تأخیر نمایی |
| ۴۲۹ | insufficient_quota | اعتبار حساب تمام شده | هرگز | توقف فوری و هشدار به تیم |
| ۴۰۰ | context_length_exceeded | ورودی از پنجرهٔ مدل بزرگتر است | خیر | کوتاهسازی تاریخچه یا بازیابی تکههای مرتبط |
| ۴۰۰ | invalid_request_error | پارامتر یا ساختار پیامها اشتباه است | خیر | فیلد param را بخوانید و همان را اصلاح کنید |
| ۲۰۰ | finish_reason=length | خروجی وسط راه قطع شده | بله، با سقف بیشتر | افزایش سقف توکن خروجی و بررسی finish_reason |
| ۵۰۰ | server_error | خطای داخلی ارائهدهنده | بله، محدود | دو تلاش و سپس گزارش |
| ۵۰۳ | service_unavailable | اشباع ظرفیت موتور | بله | تأخیر نمایی و مدل جایگزین |
| — | timeout / connection | کندی شبکه یا پاسخ طولانی | بله | تایماوت بالاتر و حالت استریم |
الگوی تلاش مجدد با تأخیر نمایی که واقعاً کار میکند
سه اصل در یک الگوی درست وجود دارد: فقط خطاهای موقتی را دوباره امتحان کن، بین تلاشها فاصلهٔ نمایی بگذار، و کمی تصادف (jitter) اضافه کن تا ده نمونهٔ برنامهتان دقیقاً همزمان دوباره تلاش نکنند. نمونهٔ Python:
import random
import time
from openai import OpenAI, APIConnectionError, APIStatusError, APITimeoutError
client = OpenAI(base_url="https://netarz.ir/api/ai/v1", api_key="sk-ntz-v1-...")
RETRYABLE_STATUS = {429, 500, 502, 503, 504}
FATAL_CODES = {"insufficient_quota", "invalid_api_key", "model_not_found"}
def error_code(exc):
body = getattr(exc, "body", None)
if isinstance(body, dict):
err = body.get("error")
if isinstance(err, dict):
return err.get("code")
return None
def backoff_seconds(attempt):
return min(2 ** attempt, 30) + random.uniform(0, 1)
def chat_with_retry(messages, model="gpt-5-mini", max_attempts=5):
last_error = None
for attempt in range(max_attempts):
try:
return client.chat.completions.create(model=model, messages=messages)
except APIStatusError as exc:
if error_code(exc) in FATAL_CODES or exc.status_code not in RETRYABLE_STATUS:
raise
last_error = exc
except (APIConnectionError, APITimeoutError) as exc:
last_error = exc
time.sleep(backoff_seconds(attempt))
raise RuntimeError(f"درخواست پس از {max_attempts} تلاش ناموفق ماند: {last_error}")
print(chat_with_retry([{"role": "user", "content": "سلام"}]).choices[0].message.content)
همان منطق در JavaScript، این بار با احترام به هدر retry-after که سرور میفرستد:
const BASE_URL = "https://netarz.ir/api/ai/v1";
const RETRYABLE = new Set([429, 500, 502, 503, 504]);
const FATAL = new Set(["insufficient_quota", "invalid_api_key", "model_not_found"]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const backoff = (attempt) => Math.min(2 ** attempt * 1000, 30000) + Math.random() * 1000;
export async function chatWithRetry(body, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
let response;
try {
response = await fetch(`${BASE_URL}/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.NETARZ_AI_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
} catch (networkError) {
await sleep(backoff(attempt));
continue;
}
if (response.ok) return response.json();
const payload = await response.json().catch(() => ({}));
const code = payload?.error?.code;
if (FATAL.has(code) || !RETRYABLE.has(response.status)) {
throw new Error(`${response.status} ${code ?? "unknown"}: ${payload?.error?.message ?? ""}`);
}
const retryAfter = Number(response.headers.get("retry-after"));
await sleep(retryAfter > 0 ? retryAfter * 1000 : backoff(attempt));
}
throw new Error("درخواست پس از چند تلاش ناموفق ماند");
}
دقت کنید که insufficient_quota عمداً در فهرست خطاهای مرگبار است. این تنها تفاوتی است که بین یک الگوی درست و یک حلقهٔ بیپایان وجود دارد.
چکلیست پنجدقیقهای وقتی چیزی کار نمیکند
- بدنهٔ کامل خطا را چاپ کنید، نه فقط پیام کوتاه کتابخانه.
error.codeوerror.paramکل ماجرا را میگویند. - همان درخواست را با curl بفرستید. اگر curl کار کرد، مشکل از کد شماست نه از سرویس.
- آدرس پایه و کلید را با هم چک کنید؛ این دو باید همیشه از یک درگاه باشند.
- نام مدل را با فهرست
GET /modelsمقایسه کنید، نه با حافظهتان. - موجودی حساب و مصرف روز جاری را ببینید تا مطمئن شوید ۴۲۹ از جنس اعتبار نیست.
- ساعت سرور را بررسی کنید؛ اختلاف زمانی زیاد گاهی باعث خطاهای عجیب در لایهٔ TLS میشود.
یک تفاوت مهم دیگر هم وجود دارد که کمتر گفته میشود: در یک ایجنت واقعی، مسئلهٔ اصلی دیگر «آیا مدل ابزار درست را انتخاب میکند» نیست، بلکه «چطور بفهمیم کجا اشتباه کرد» است. برای همین از همان روز اول هر دور از حلقه را لاگ کنید؛ نام ابزار، آرگومانها، خروجی و مدت اجرا. وقتی یک روز پاسخ عجیبی به دست کاربر رسید، این لاگ تنها چیزی است که نجاتتان میدهد.
قدم بعدی
بیشتر این دوازده خطا با سه عادت ساده از بین میرود: لاگ کردن error.code، جدا کردن خطاهای موقتی از دائمی، و نگه داشتن موجودی حساب بالای صفر. اگر تازه شروع کردهاید، ابتدا با راهنمای شارژ اعتبار OpenAI API و راهنمای دریافت کلید API در ایران مسیر را ببندید و بعد سراغ سختکردن کد بروید.
مستندات فنی درگاه، شامل فهرست خطاها و نمونهٔ درخواستها، در مستندات API نِت اَرز در دسترس است و اعتبار حساب را میتوانید از صفحهٔ افزایش اعتبار وبسرویس ChatGPT شارژ کنید؛ قیمت لحظهای همانجا درج شده است. برای پروژههای واقعی توصیهٔ ما این است که پیش از انتشار، همین الگوی تلاش مجدد را اضافه کنید و یک هشدار موجودی بگذارید؛ همین دو کار جلوی بیشتر تماسهای ساعت سه بامداد را میگیرد.
آموزشهای کاربردی در گیسو
گیسو برند هوش مصنوعی نِت اَرز است، با همین حساب و همین اعتبار. این راهنماها قدمبهقدم نشان میدهند همین کار را چطور با هوش مصنوعی انجام دهید.
پرسشهای پرتکرار
خطای 429 همیشه یعنی اعتبارم تمام شده؟
نه. کد ۴۲۹ دو حالت کاملاً متفاوت دارد که فقط با فیلد error.code از هم جدا میشوند. اگر code برابر rate_limit_exceeded باشد یعنی سرعت درخواستها زیاد بوده و با کمی صبر حل میشود، اما insufficient_quota یعنی موجودی حساب صفر است و تلاش مجدد هیچ فایدهای ندارد.
چرا با وجود درست بودن کلید، خطای 403 میگیرم؟
خطای ۴۰۳ با کد unsupported_country_region_territory به کلید شما ربطی ندارد؛ ارائهدهنده IP مبدأ درخواست را نپذیرفته است. یا باید کد را روی سروری اجرا کنید که در محدودهٔ مسدود نیست، یا از یک درگاه API استفاده کنید که خودش درخواست را ارسال میکند.
چطور جلوی خطای context_length_exceeded را بگیرم؟
تاریخچهٔ گفتگو را بینهایت نگه ندارید. یا فقط چند پیام آخر را بفرستید، یا پیامهای قدیمی را خلاصه کنید و خلاصه را جایگزین کنید. اگر با اسناد بزرگ کار میکنید، بهجای فرستادن کل سند فقط تکههای مرتبط را بازیابی کنید؛ متن فارسی هم به ازای هر کلمه توکن بیشتری از انگلیسی مصرف میکند و زودتر به سقف میرسد.
چند بار و با چه فاصلهای تلاش مجدد کنم؟
برای خطاهای موقتی معمولاً چهار تا پنج تلاش کافی است، با فاصلهٔ نمایی یک، دو، چهار و هشت ثانیه و کمی تأخیر تصادفی. اگر سرور هدر retry-after فرستاد، همان عدد را رعایت کنید و برای خطاهای دائمی مثل کلید نامعتبر اصلاً تلاش مجدد نکنید.
خطای «بودجهٔ ماهانهٔ این پروژه تمام شده است» یعنی اعتبارم تمام شده؟
نه. این پیام یعنی هزینهٔ این ماه پروژه به بودجهای رسیده که خودتان برایش گذاشتهاید (کد project_budget_exceeded). بودجه را در صفحهٔ پروژه بالا ببرید یا خالی بگذارید تا سقف برداشته شود؛ وگرنه از اول ماه میلادی بعد دوباره کار میکند.
نظر خوانندگان
هنوز نظری ثبت نشده. اگر این مقاله پرسشتان را جواب داد یا جای چیزی در آن خالی ماند، همینجا بنویسید.