> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aisonar.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Handle API errors gracefully

## Error response families

Chat Completions, Responses, Messages, and Gemini are public protocol boundaries, not Web dashboard APIs. They return the error shape of the requested protocol and are never wrapped in the Web application's `{ success, data, error }` envelope. service validation errors are preserved whenever they can be returned safely.

OpenAI-compatible Chat Completions and Responses gateway errors use the OpenAI-style shape below, with optional [Agent-First hints](/guides/agent-first-api):

```json theme={null}
{
  "error": {
    "message": "Human-readable error description",
    "type": "error_type",
    "code": "error_code",
    "param": "parameter_name",
    "did_you_mean": "suggested_model",
    "suggestions": [{"id": "model-id"}],
    "hint": "Next step guidance",
    "retryable": true,
    "retry_after": 30
  }
}
```

For gateway-originated OpenAI-compatible errors, the required base fields (`message`, `type`) are present. `code` and `param` are optional and appear only when relevant. The hint fields (`did_you_mean`, `suggestions`, `hint`, `retryable`, `retry_after`, `balance_usd`, `estimated_cost_usd`) are optional extensions for AI agent self-correction. See the [Agent-First API guide](/guides/agent-first-api) for details.

Anthropic Messages and Gemini `/v1beta` endpoints use their native error families and response shapes. Do not write one parser that assumes every AI Sonar endpoint uses the OpenAI shape above.

AI Sonar does not retry deterministic service 400/422 responses. Rate limits, 5xx responses, and timeouts may use bounded retries before the response starts. No protocol retries occur after response bytes have been delivered.

## HTTP Status Codes

| Code | Description                                           |
| ---- | ----------------------------------------------------- |
| 400  | Bad Request - Invalid parameters                      |
| 401  | Unauthorized - Invalid or missing API key             |
| 402  | Payment Required - Insufficient balance               |
| 403  | Forbidden - Access denied or model not allowed        |
| 404  | Not Found - Model or resource not found               |
| 413  | Payload Too Large - Input or file size exceeded       |
| 429  | Too Many Requests - Rate limit exceeded               |
| 500  | Internal Server Error                                 |
| 502  | Bad Gateway - Service error                           |
| 503  | Service Unavailable - Service temporarily unavailable |
| 504  | Gateway Timeout - Request timed out                   |

## Error Types

### Authentication Errors (401)

| Type              | Code              | Description                   |
| ----------------- | ----------------- | ----------------------------- |
| `invalid_api_key` | `invalid_api_key` | API key is missing or invalid |
| `expired_api_key` | `expired_api_key` | API key has been revoked      |

```python theme={null}
from openai import OpenAI, AuthenticationError

try:
    response = client.chat.completions.create(...)
except AuthenticationError as e:
    print(f"Authentication failed: {e.message}")
```

### Payment Errors (402)

| Type                   | Code                   | Description                 |
| ---------------------- | ---------------------- | --------------------------- |
| `insufficient_balance` | `insufficient_balance` | Account balance is too low  |
| `quota_exceeded`       | `quota_exceeded`       | API key usage limit reached |

```python theme={null}
from openai import OpenAI, APIStatusError

try:
    response = client.chat.completions.create(...)
except APIStatusError as e:
    if e.status_code == 402:
        print("Please top up your account balance")
```

### Access Errors (403)

| Type            | Code                | Description                        |
| --------------- | ------------------- | ---------------------------------- |
| `access_denied` | `access_denied`     | Access to resource denied          |
| `access_denied` | `model_not_allowed` | Model not allowed for this API key |

```json theme={null}
{
  "error": {
    "message": "You don't have permission to access this model",
    "type": "access_denied",
    "code": "model_not_allowed"
  }
}
```

### Validation Errors (400)

| Type                      | Description                                                   |
| ------------------------- | ------------------------------------------------------------- |
| `invalid_request_error`   | Request parameters are invalid                                |
| `context_length_exceeded` | Input too long for model                                      |
| `model_not_found`         | Requested model is not available in the current model details |

```json theme={null}
{
  "error": {
    "message": "Model not found: please check the model name",
    "type": "invalid_request_error",
    "param": "model",
    "code": "model_not_found",
    "did_you_mean": "gpt-5.4",
    "suggestions": [{"id": "gpt-5.4"}, {"id": "gpt-5-mini"}],
    "hint": "Did you mean 'gpt-5.4'? Use GET https://api.aisonar.dev/v1/models to list all available models."
  }
}
```

Public routes do not distinguish typo, hidden, deferred, or non-public model states in the response body. If a model is not currently available through the model details, AI Sonar returns `model_not_found`.

### Rate Limit Errors (429)

When you exceed rate limits:

```json theme={null}
{
  "error": {
    "message": "Rate limit: 1000 rpm exceeded",
    "type": "rate_limit_exceeded",
    "code": "rate_limit_exceeded",
    "retryable": true,
    "retry_after": 8,
    "hint": "Rate limited. Retry after 8s. Current limit: 1000/min for user role."
  }
}
```

**Headers included:**

```
Retry-After: 8
```

The `Retry-After` header and `retry_after` field both indicate the exact seconds to wait before retrying.

### Payload Too Large (413)

When input or file size exceeds limits:

```json theme={null}
{
  "error": {
    "message": "Input size exceeds maximum allowed",
    "type": "invalid_request_error",
    "code": "payload_too_large"
  }
}
```

Common causes:

* Image file too large (max 20MB)
* Audio file too large (max 25MB)
* Input text exceeds model context length

### Service Errors (5xx)

| Type            | Description                                |
| --------------- | ------------------------------------------ |
| `server_error`  | The service could not complete the request |
| `timeout_error` | The request timed out                      |

Retry only when `retryable` is `true`. If `retry_after` is present, wait for the specified number of seconds. Use `alternatives` to select another available model when provided.

## Handling Errors in Python

```python theme={null}
from openai import OpenAI, APIError, RateLimitError, APIConnectionError

client = OpenAI(
    api_key="sk-your-api-key",
    base_url="https://api.aisonar.dev/v1"
)

def chat_with_retry(messages, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.chat.completions.create(
                model="gpt-4o",
                messages=messages
            )
        except RateLimitError as e:
            if attempt < max_retries - 1:
                import time
                time.sleep(2 ** attempt)  # Exponential backoff
                continue
            raise
        except APIConnectionError as e:
            print(f"Connection error: {e}")
            raise
        except APIError as e:
            print(f"API error: {e.status_code} - {e.message}")
            raise
```

## Handling Errors in JavaScript

```javascript theme={null}
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: 'sk-your-api-key',
  baseURL: 'https://api.aisonar.dev/v1'
});

async function chatWithRetry(messages, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.chat.completions.create({
        model: 'gpt-4o',
        messages
      });
    } catch (error) {
      if (error instanceof OpenAI.RateLimitError) {
        if (attempt < maxRetries - 1) {
          await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
          continue;
        }
      }
      throw error;
    }
  }
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Implement exponential backoff">
    When rate limited, wait progressively longer between retries:

    ```python theme={null}
    wait_time = 2 ** attempt  # 1s, 2s, 4s, 8s...
    ```
  </Accordion>

  <Accordion title="Set timeouts">
    Always set reasonable timeouts to avoid hanging requests:

    ```python theme={null}
    client = OpenAI(timeout=60.0)  # 60 second timeout
    ```
  </Accordion>

  <Accordion title="Log errors for debugging">
    Log the full error response including request ID for support:

    ```python theme={null}
    except APIError as e:
        logger.error(f"API Error: {e.status_code} - {e.message}")
    ```
  </Accordion>

  <Accordion title="Handle model-specific errors">
    Some models have specific requirements (e.g., max tokens, image formats).
    Validate inputs before making requests.
  </Accordion>
</AccordionGroup>
