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

# Error Handling

> Understand error responses, error codes, and retry strategies

## Error Response Format

All errors follow a consistent JSON structure:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request parameters",
    "details": {
      "field": "prompt",
      "reason": "Required field is missing"
    }
  }
}
```

## HTTP Status Codes

| Status | Meaning               | When                       |
| ------ | --------------------- | -------------------------- |
| `400`  | Bad Request           | Invalid parameters         |
| `401`  | Unauthorized          | Missing or invalid API key |
| `402`  | Payment Required      | Insufficient balance       |
| `403`  | Forbidden             | Key lacks permissions      |
| `404`  | Not Found             | Resource does not exist    |
| `409`  | Conflict              | Resource state conflict    |
| `422`  | Unprocessable Entity  | Business logic error       |
| `429`  | Too Many Requests     | Rate limit exceeded        |
| `500`  | Internal Server Error | Unexpected server error    |
| `502`  | Bad Gateway           | Provider returned an error |
| `503`  | Service Unavailable   | Provider temporarily down  |

## Error Codes

### Authentication Errors (401)

| Code                 | Description                            |
| -------------------- | -------------------------------------- |
| `AUTH_REQUIRED`      | No Authorization header provided       |
| `AUTH_INVALID_TOKEN` | API key is malformed or does not exist |
| `AUTH_TOKEN_EXPIRED` | API key has passed its expiry date     |

### Permission Errors (403)

| Code                    | Description                                     |
| ----------------------- | ----------------------------------------------- |
| `FORBIDDEN`             | Action not allowed                              |
| `KEY_PERMISSION_DENIED` | API key lacks required provider/type permission |
| `KEY_EXPIRED`           | API key has expired                             |
| `KEY_REVOKED`           | API key has been deleted                        |

### Resource Errors (404)

| Code                 | Description                    |
| -------------------- | ------------------------------ |
| `NOT_FOUND`          | Generic resource not found     |
| `MODEL_NOT_FOUND`    | Requested model does not exist |
| `TASK_NOT_FOUND`     | Task ID does not exist         |
| `PROVIDER_NOT_FOUND` | Provider does not exist        |

### Validation Errors (400/422)

| Code                | Description                                     |
| ------------------- | ----------------------------------------------- |
| `VALIDATION_ERROR`  | Request body failed validation                  |
| `INVALID_PARAMETER` | A parameter value is out of range or wrong type |
| `MISSING_PARAMETER` | A required parameter is missing                 |

### Payment Errors (402)

| Code                   | Description                              |
| ---------------------- | ---------------------------------------- |
| `INSUFFICIENT_BALANCE` | Account balance too low for this request |
| `PAYMENT_REQUIRED`     | No payment method on file                |
| `PAYMENT_FAILED`       | Charge attempt failed                    |

### Business Logic Errors (422)

| Code                 | Description                            |
| -------------------- | -------------------------------------- |
| `QUOTA_EXCEEDED`     | Monthly quota for this API key reached |
| `FILE_TOO_LARGE`     | Uploaded file exceeds size limit       |
| `UNSUPPORTED_FORMAT` | File format not supported              |

### Rate Limit Errors (429)

| Code                  | Description                   |
| --------------------- | ----------------------------- |
| `RATE_LIMIT_EXCEEDED` | Too many requests — slow down |

### Conflict Errors (409)

| Code                 | Description                      |
| -------------------- | -------------------------------- |
| `TASK_NOT_RETRYABLE` | Only failed tasks can be retried |

### Provider Errors (502)

| Code                    | Description                         |
| ----------------------- | ----------------------------------- |
| `PROVIDER_ERROR`        | Upstream provider returned an error |
| `PROVIDER_UNAVAILABLE`  | Provider is temporarily offline     |
| `PROVIDER_TIMEOUT`      | Provider did not respond in time    |
| `PROVIDER_RATE_LIMITED` | Provider's own rate limit hit       |

### Internal Errors (500)

| Code             | Description             |
| ---------------- | ----------------------- |
| `INTERNAL_ERROR` | Unexpected server error |

## Retry Strategy

### Retryable vs Non-Retryable

| Retryable               | Non-Retryable              |
| ----------------------- | -------------------------- |
| `429` Rate limited      | `400` Validation error     |
| `500` Internal error    | `401` Authentication error |
| `502` Provider error    | `402` Payment error        |
| `503` Unavailable       | `403` Permission error     |
| `PROVIDER_TIMEOUT`      | `404` Not found            |
| `PROVIDER_RATE_LIMITED` | `409` Conflict             |

### Exponential Backoff

For retryable errors, use exponential backoff:

```javascript theme={null}
async function requestWithRetry(fn, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxRetries || !isRetryable(error.status)) {
        throw error;
      }
      const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}

function isRetryable(status) {
  return [429, 500, 502, 503].includes(status);
}
```

### Rate Limit Handling

When you receive a `429`, check the `X-RateLimit-Reset` header for when you can retry:

```javascript theme={null}
if (response.status === 429) {
  const resetTimestamp = response.headers.get("X-RateLimit-Reset");
  const waitMs = parseInt(resetTimestamp, 10) * 1000 - Date.now();
  await new Promise((resolve) => setTimeout(resolve, Math.max(waitMs, 1000)));
}
```
