---
title: خطاها
description: هر کد خطای API دانا، علتش و کاری که باید بکنید، با نمونه پاکت خطا
---

هر خطا یک پاسخ JSON با کد HTTP درست است. دو رابط دانا دو شکل پاکت جدا دارند: مسیرهای `/v1/messages` پاکت Anthropic می‌گیرند و بقیه مسیرها پاکت OpenAI.

## جدول تشخیص سریع

| کد | چه اتفاقی افتاده | کار بعدی |
|---|---|---|
| `400` | بدنه درخواست ناقص یا نامعتبر است | پیام خطا فیلد مقصر را می‌گوید؛ همان را درست کنید |
| `401` | کلید نیست یا شناخته نشد | هدر `Authorization: Bearer ...` را بررسی کنید |
| `402` | اعتبار مسیر بالادست تمام شده | تلاش مجدد بی‌فایده است؛ با پشتیبانی تماس بگیرید |
| `403` | کلید معتبر است ولی این مسیر را ندارد | مسیرهای `internal` فقط برای سرویس‌های داخلی دانا هستند |
| `404` | مسیر یا منبع وجود ندارد | آدرس و شناسه مدل را بازبینی کنید |
| `413` | بدنه درخواست از سقف بزرگ‌تر است | پیام‌ها را کوتاه یا تکه‌تکه کنید |
| `429` | نرخ درخواست، ظرفیت، سقف توکن یا اعتبار | به [بخش ۴۲۹](#کد-429-چهار-حالت-جدا) بروید؛ چهار حالت جداگانه دارد |
| `500` | خطای پیش‌بینی‌نشده در گیت‌وی | با backoff تا سه بار تلاش مجدد کنید |
| `502` | گیت‌وی به موتور مدل نرسید | تلاش مجدد کنید؛ اگر ادامه داشت گزارش بدهید |
| `503` | سرویس‌دهی مدل هنوز روشن نشده | تلاش مجدد نکنید؛ `code` را بخوانید |
| `504` | موتور مدل به‌موقع جواب نداد | درخواست را کوچک‌تر کنید و دوباره بفرستید |

## شکل پاکت خطا

**رابط OpenAI** (`/v1/chat/completions`، `/v1/models` و بقیه):

```json
{
  "error": {
    "message": "Invalid API key.",
    "type": "authentication_error",
    "param": null,
    "code": null
  }
}
```

`param` همیشه `null` است. فیلد `code` فقط در چند خطای مشخص مقدار می‌گیرد و در بقیه `null` می‌ماند.

**رابط Anthropic** (`/v1/messages` و `/v1/messages/count_tokens`):

```json
{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key."
  }
}
```

دقت کنید که پاکت Anthropic یک `type: "error"` در سطح بیرونی هم دارد. SDK رسمی Anthropic روی همین فیلد حساب می‌کند.

### مقدار `type` بر اساس کد

| کد | رابط OpenAI | رابط Anthropic |
|---|---|---|
| `400` | `invalid_request_error` | `invalid_request_error` |
| `401` | `authentication_error` | `authentication_error` |
| `403` | `permission_error` | `permission_error` |
| `404` | `not_found_error` | `not_found_error` |
| `409` | `conflict_error` | `api_error` |
| `413` | `invalid_request_error` | `request_too_large` |
| `429` | `rate_limit_error` | `rate_limit_error` |
| بقیه | `api_error` | `api_error` |

## خطاها به تفکیک کد

### `400` - درخواست نامعتبر

پیام خطا دقیقا می‌گوید کجا ایراد دارد. رایج‌ترین‌ها:

```json
{"error": {"message": "Request body must include a 'messages' array.", "type": "invalid_request_error", "param": null, "code": null}}
```

| پیام | علت | راه‌حل |
|---|---|---|
| `Request body must include a 'messages' array.` | بدنه JSON نیست یا `messages` ندارد | ساختار بدنه را با [Chat Completions](/docs/chat-completions) بسنجید |
| `Request body must be a JSON object.` | بدنه `count_tokens` آبجکت نیست | یک آبجکت JSON بفرستید |
| `Invalid X-Dana-Harness value ...` | مقدار هدر harness اشتباه است | یکی از سه مقدار `none` یا `base` یا `agent` |
| `Invalid effort ...` | سطح effort ناشناخته است | چهار سطح مجاز `low` و `medium` و `high` و `max` |

خطاهای اعتبارسنجی خودکار (نوع اشتباه یک پارامتر، مثلا `temperature` رشته‌ای) هم همین `400` را می‌گیرند و متن خطا فهرست فیلدهای مقصر را برمی‌گرداند.

### `401` - احراز هویت ناموفق

```json
{"error": {"message": "Missing API key.", "type": "authentication_error", "param": null, "code": null}}
```

چهار حالت جدا با چهار پیام جدا:

- `Missing API key.` هدر `Authorization` ارسال نشده.
- `Invalid API key.` کلید در پایگاه داده پیدا نشد؛ شاید غیرفعال شده یا حرفی جا افتاده.
- `internal_app key requires X-Dana-User-Id and X-Dana-Session-Id.` مربوط به کلیدهای داخلی است.
- `X-Dana-User-Id is only valid with an internal_app key.` این هدر را از درخواست بردارید.

اگر کلید تازه ساخته‌اید و همچنان `401` می‌گیرید، فاصله یا خط جدید اضافه در متغیر محیطی را بررسی کنید. شرح کامل را در [احراز هویت](/docs/authentication) بخوانید.

### `402` - اعتبار بالادست تمام شده

```json
{"error": {"message": "Router credit/quota exhausted.", "type": "insufficient_quota", "param": null, "code": "router_credit_exhausted"}}
```

این خطا به حساب شما ربط ندارد؛ مربوط به مسیر بالادست دانا است. تلاش مجدد جوابش را عوض نمی‌کند.

### `403` - دسترسی ممنوع

کلید درست است اما اجازه این مسیر را ندارد. مسیرهای `/internal/...` فقط با کلید `internal_app` باز می‌شوند و برای مصرف‌کننده عمومی API در دسترس نیستند.

### `413` - بدنه بیش از حد بزرگ

گیت‌وی پیش از خواندن بدنه، `Content-Length` را با سقف پیکربندی‌شده می‌سنجد و درخواست را همان‌جا رد می‌کند.

```json
{"error": {"message": "Request body too large.", "type": "invalid_request_error", "param": null, "code": null}}
```

تاریخچه مکالمه را کوتاه کنید یا متن‌های بلند را تکه‌تکه بفرستید.

### کد `429` - چهار حالت جدا

`429` چهار علت متفاوت دارد. اول `type` را بخوانید، بعد تصمیم بگیرید.

**۱. عبور از نرخ درخواست کلید** - `type: "rate_limit_error"`

```json
{"error": {"message": "Rate limit exceeded.", "type": "rate_limit_error", "param": null, "code": null}}
```

این پاسخ چهار هدر هم دارد:

- `Retry-After`
- `X-RateLimit-Limit`
- `X-RateLimit-Remaining`
- `X-RateLimit-Reset`

به جای عدد ثابت، به `Retry-After` گوش بدهید. جزئیات را در [محدودیت‌ها](/docs/rate-limits) ببینید.

**۲. اتمام اعتبار** - `type: "insufficient_quota"`

```json
{"error": {"message": "You have insufficient credit. Please top up your balance.", "type": "insufficient_quota", "param": null, "code": "insufficient_quota"}}
```

تلاش مجدد کمکی نمی‌کند. از [بخش اعتبار کنسول](https://console.dana.expert/credit) شارژ کنید.

**۳. رسیدن به سقف پنجره توکن** - `type: "window_cap"`

این حالت پاکت مخصوص خودش را دارد و روی رابط OpenAI عینا برگردانده می‌شود:

```json
{
  "error": {
    "type": "window_cap",
    "which": "session_4h",
    "reset_at": "<ISO 8601 timestamp>",
    "upgrade_url": "https://console.dana.expert/plans"
  }
}
```

| فیلد | معنا |
|---|---|
| `which` | کدام پنجره پر شده: `session_4h` یا `weekly` |
| `reset_at` | لحظه دقیق باز شدن پنجره، به وقت UTC |
| `upgrade_url` | نشانی ارتقای پلن در کنسول |

تا رسیدن `reset_at` صبر کنید؛ تلاش زودتر همین جواب را می‌گیرد. روی رابط Anthropic همین خطا با پاکت استاندارد Anthropic و نوع `rate_limit_error` برمی‌گردد.

**۴. پرشدن ظرفیت لحظه‌ای** - `type: "capacity_busy"`

فیلد `eta_seconds` تخمین زمان خالی شدن صف را می‌دهد؛ این یکی ارزش تلاش مجدد دارد.

### `500` - خطای داخلی

```json
{"error": {"message": "Internal server error.", "type": "api_error", "param": null, "code": null}}
```

پاسخ عمدا جزئیات ندارد. `x-request-id` را از هدر پاسخ بردارید و همان را به پشتیبانی بدهید؛ لاگ سمت ما با همین شناسه پیدا می‌شود.

### `502` و `504` - مشکل موتور مدل

| کد | پیام | `type` | معنا |
|---|---|---|---|
| `502` | `Cannot reach the model backend.` | `api_error` | گیت‌وی نتوانست به موتور وصل شود |
| `504` | `Upstream model timed out.` | `timeout_error` | موتور در مهلت مقرر جواب نداد |

هر دو گذرا هستند. روی `504` علاوه بر تلاش مجدد، `max_tokens` را کمتر کنید.

### `503` - سرویس‌دهی مدل روشن نیست

```json
{"error": {"message": "Dana model serving is coming soon.", "type": "api_error", "param": null, "code": "coming_soon"}}
```

روی رابط Anthropic:

```json
{"type": "error", "error": {"type": "api_error", "message": "Dana model serving is coming soon."}}
```

در این حالت چهار مسیر مدل بسته‌اند:

- `/v1/chat/completions`
- `/v1/completions`
- `/v1/messages`
- `/v1/models`

بقیه سرویس سر جایش است: ساخت کلید، شارژ اعتبار و گزارش مصرف کار می‌کنند. تلاش مجدد روی این خطا بی‌فایده است.

## سیاست تلاش مجدد

```python
import random
import time

import httpx

RETRYABLE = {429, 500, 502, 503, 504}


def call(payload, api_key, max_attempts=3):
    for attempt in range(max_attempts):
        response = httpx.post(
            "https://api.dana.expert/v1/chat/completions",
            headers={"Authorization": f"Bearer {api_key}"},
            json=payload,
            timeout=120,
        )
        if response.status_code not in RETRYABLE:
            response.raise_for_status()
            return response.json()

        body = response.json().get("error", {})
        # این حالت‌ها با تلاش مجدد درست نمی‌شوند.
        if body.get("type") == "window_cap" or body.get("code") in (
            "coming_soon",
            "insufficient_quota",
        ):
            raise RuntimeError(body.get("message"))

        wait = float(response.headers.get("Retry-After", 2**attempt))
        time.sleep(wait + random.random())

    raise RuntimeError("سه تلاش پیاپی ناموفق بود")
```

قاعده‌های پشت این کد:

- روی `429` با `type: "rate_limit_error"` یا `capacity_busy` تلاش مجدد کنید، با backoff نمایی و کمی jitter.
- روی `429` با `window_cap` یا `insufficient_quota` تلاش مجدد نکنید؛ اولی زمان مشخص دارد، دومی شارژ می‌خواهد.
- روی `503` با `code` برابر `coming_soon` صبر کنید تا اعلام راه‌اندازی.
- `4xx` غیر از `429` را در کد خودتان مدیریت کنید. همان درخواست، همان جواب.
- `x-request-id` هر پاسخ را لاگ کنید. بدون آن، پیگیری یک خطای تک‌باره تقریبا ناممکن است.

## صفحه‌های مرتبط

- [شروع سریع](/docs/quickstart) - نخستین درخواست موفق
- [احراز هویت](/docs/authentication) - ریشه بیشتر خطاهای `401` و `403`
- [محدودیت‌ها](/docs/rate-limits) - جزئیات `429` و هدرهای نرخ
- [قیمت و توکن](/docs/pricing-and-tokens) - اعتبار و شمارش توکن
