> For clean Markdown of any page, append `.md` to the page URL.
> For a complete documentation index, see https://docs.sarvam.ai/llms.txt.
> For full documentation content in one file, see https://docs.sarvam.ai/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server.

# Errors & Troubleshooting

> Central reference for Sarvam API error codes, HTTP status handling, SDK exceptions, retries, and common integration pitfalls.

Use this page when a request fails or behaves unexpectedly. Endpoint reference pages document per-field validation; this page explains **what failed**, **how to fix it**, and **how to retry safely**.

## Error response shape

Most REST errors return JSON:

```json
{
  "error": {
    "message": "Human-readable description",
    "code": "invalid_request_error"
  }
}
```

| `error.code`                 | Typical HTTP status | Meaning                                  |
| ---------------------------- | ------------------- | ---------------------------------------- |
| `invalid_api_key_error`      | **403**             | Missing, malformed, or unknown API key   |
| `invalid_request_error`      | 400 / 413 / 422     | Bad parameters, payload, or file         |
| `unprocessable_entity_error` | 422                 | Schema valid but business rule failed    |
| `insufficient_quota_error`   | 429                 | Credits exhausted                        |
| `rate_limit_exceeded_error`  | 429 / 503           | Too many requests or high load           |
| `internal_server_error`      | 500                 | Server-side failure — retry with backoff |

**Auth failures use HTTP `403`, not `401`.** Treat `403` with `invalid_api_key_error` as an authentication problem. See [Authentication](/api-reference-docs/authentication#status-codes-for-authentication-failures).

## HTTP status quick reference

| Status    | When it happens                           | What to do                                                            |
| --------- | ----------------------------------------- | --------------------------------------------------------------------- |
| 400       | Invalid body or parameters                | Fix request; do not retry blindly                                     |
| 403       | Invalid API key (or forbidden)            | Check `error.code` and key header                                     |
| 422       | Validation failed (e.g. wrong field name) | Compare with OpenAPI / guides                                         |
| 429       | Rate limit or quota                       | Back off; see [Credits & Rate Limits](/api-reference-docs/ratelimits) |
| 500 / 503 | Transient server issues                   | Retry with exponential backoff                                        |

## SDK handling (Python)

```python
from sarvamai import SarvamAI
from sarvamai.core.api_error import ApiError

client = SarvamAI(api_subscription_key="YOUR_SARVAM_API_KEY")

try:
    response = client.speech_to_text.transcribe(
        file=open("audio.wav", "rb"),
        model="saaras:v3",
        mode="transcribe",
    )
except ApiError as e:
    if e.status_code == 403:
        print("Check SARVAM_API_KEY — auth uses 403, not 401")
    elif e.status_code == 429:
        print("Rate limited — retry after delay")
    else:
        print(e.status_code, e.body)
```

## SDK handling (JavaScript)

```javascript
import { SarvamAIClient } from "sarvamai";

const client = new SarvamAIClient({
  apiSubscriptionKey: process.env.SARVAM_API_KEY,
});

try {
  const response = await client.speechToText.transcribe({
    file: audioFile,
    model: "saaras:v3",
    mode: "transcribe",
  });
} catch (err) {
  // SDK wraps HTTP errors — inspect status and body
  console.error(err.statusCode, err.body);
}
```

## Retry with exponential backoff

Use retries only for **429**, **500**, and **503** — not for 400/403/422.

```python
import time

def call_with_backoff(fn, max_retries=5):
    delay = 1.0
    for attempt in range(max_retries):
        try:
            return fn()
        except ApiError as e:
            if e.status_code not in (429, 500, 503) or attempt == max_retries - 1:
                raise
            time.sleep(delay)
            delay = min(delay * 2, 60)
```

```javascript
async function callWithBackoff(fn, maxRetries = 5) {
  let delay = 1000;
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (err) {
      const status = err.statusCode ?? err.status;
      if (![429, 500, 503].includes(status) || attempt === maxRetries - 1) throw err;
      await new Promise((r) => setTimeout(r, delay));
      delay = Math.min(delay * 2, 60000);
    }
  }
}
```

## Common pitfalls

| Symptom                        | Likely cause                               | Fix                                                                                                                                                    |
| ------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 403 on every call              | Wrong or missing `api-subscription-key`    | Set `SARVAM_API_KEY`; handle **403** not 401                                                                                                           |
| 422 on doc digitization        | Used `language_code` instead of `language` | Use `job_parameters.language` — see [Document Digitization](/api-reference-docs/api-guides-tutorials/document-digitization/overview)                   |
| 400 `output_format`            | Sent `"markdown"`                          | Use `"md"` or `"html"` only                                                                                                                            |
| 405 on doc/STT download        | Used GET                                   | Use **POST** with `job_id` + `files` array                                                                                                             |
| TTS returns base64 string      | Expected raw bytes                         | `base64.b64decode(response.audios[0].audio)` — see [TTS REST](/api-reference-docs/api-guides-tutorials/text-to-speech/rest-api)                        |
| Chat 400                       | Missing `model`                            | Pass `model="sarvam-105b"` (or `sarvam-30b`)                                                                                                           |
| Pronunciation dict JS 400      | Blob without JSON MIME                     | Use `fetch` + `FormData` workaround — see [Pronunciation Dictionary](/api-reference-docs/api-guides-tutorials/text-to-speech/pronunciation-dictionary) |
| Batch webhook empty transcript | Expected text in webhook                   | Webhook is `JobStatusResponse` only — [download](/api-reference-docs/api-guides-tutorials/speech-to-text/batch-api#webhook-payload) output JSON files  |

## Support workflow

1. Note **timestamp**, **endpoint**, and **`error.code`** from the response body.
2. Reproduce with curl or SDK using the smallest failing request.
3. Check [API Status](https://status.sarvam.ai/) for incidents.
4. Contact support via [Talk to us](/api-reference-docs/help) with request ID if available.

## Related pages

* [Authentication](/api-reference-docs/authentication)
* [Credits & Rate Limits](/api-reference-docs/ratelimits)
* [Pricing](/api-reference-docs/pricing)
* [SDKs & Libraries](/api-reference-docs/getting-started/sd-ks-libraries)