> 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 | | `not_found_error` | 404 | Resource does not exist (e.g. unknown job ID) | | `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 | Some endpoints add their own codes for product-specific rules, for example Document Digitization returns `invalid_request_error` (400) when a file exceeds the 10-page limit. These are listed in the "Errors" section of each API guide. > **Warning** > > **Auth failures use HTTP `403`, not `401`.** Treat `403` with `invalid_api_key_error` as an authentication problem. See [Authentication](/api-reference/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/getting-started/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:v4", 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:v4", 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 ```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 ```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 `language`, flat in [Document AI](/api/api-guides-tutorials/document-intelligence/overview), nested as `job_parameters.language` in the legacy API | | 400 `output_format` | Sent `"markdown"` | Use `"md"` or `"html"` only (digitise rejects `"json"`) | | 405 on doc/STT download | Used GET | Legacy API: use **POST** with `job_id` + `files` array. Document AI: `GET /doc-ai/v1/job/{job_id}/download-url` | | 409 on doc translation export | Translation not yet `Completed` for that language | Check each language's `state` in `translations[]` from `live-status`; export only languages where `state` is `Completed` | | Doc translation stuck in `Accepted` | File never uploaded or `start` never called | `PUT` the file to `upload_url`, then call `POST /translate/document/jobs/{job_id}/start` | | TTS returns base64 string | Expected raw bytes | `base64.b64decode(response.audios[0].audio)`. See [TTS REST](/api/api-guides-tutorials/text-to-speech/rest-api) | | Voice Cloning returns base64 string | Expected raw bytes | `base64.b64decode(response.audio)`. See [Voice Cloning overview](/api/api-guides-tutorials/voice-cloning/overview) | | Voice Cloning 400 for missing voice | No `voice_id` was sent | Create a voice with `POST /voices/create`, then pass its `voice_id` to `POST /voices/clone`. See [Voice Cloning overview](/api/api-guides-tutorials/voice-cloning/overview) | | Chat 400 | Missing `model` | Pass `model="sarvam-105b"` or `model="sarvam-105b-conversations"` | | Chat V2 400 | Serde / type mismatch in JSON body | Fix field types to match [Chat Completion V2](/api-reference/chat/chat-completions-v2); read `error.message` (e.g. boolean vs string). Include `error.request_id` in support tickets when present. | | Chat V2 400 (`glm5.3`) | `logprobs: true`, or **`top_logprobs` present** (including `0`) | Omit **`top_logprobs` entirely**; use `logprobs: false` or omit `logprobs`. See [GLM-5.3](/api/getting-started/models/openweight/glm-5-3#known-limitations) | | Chat V2 503 (`glm5.3`) | More than four `stop` sequences | Send at most four stops; note SDKs may **auto-retry** `503` for this case | | Pronunciation dict JS 400 | Blob without JSON MIME | Use `fetch` + `FormData` workaround. See [Pronunciation Dictionary](/api/api-guides-tutorials/text-to-speech/pronunciation-dictionary) | | Batch webhook empty transcript | Expected text in webhook | Webhook is `JobStatusResponse` only, [download](/api/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/getting-started/help) with request ID if available. ## Related pages * [Authentication](/api-reference/authentication) * [Credits & Rate Limits](/api/getting-started/ratelimits) * [Pricing](/api/getting-started/pricing) * [SDKs & Libraries](/api/getting-started/sdks) > Central reference for Sarvam API error codes, HTTP status handling, SDK exceptions, retries, and common integration pitfalls.