Error handling on SageMaker

View as Markdown

Every Sarvam model container on SageMaker uses the same error contract. Read this once, then see the per-model references — Speech-to-Text, Text-to-Speech, and Sarvam Vision — for the model-specific bodies.

Every non-2xx is delivered as HTTP 424

Through sagemaker-runtime:InvokeEndpoint, any error the container returns is collapsed by boto3 into a ModelError (HTTP 424). The container’s real status is in OriginalStatusCode, its JSON body in OriginalMessage, and its Retry-After header is dropped — so retry hints ride in the body as retry_after.

Branch on OriginalStatusCode + error.code, never on the HTTP status (it is always 424).

from botocore.exceptions import ClientError
import json
try:
r = client.invoke_endpoint(EndpointName=EP, ContentType=ct, Body=body)
except ClientError as e:
status = e.response["OriginalStatusCode"] # 400 / 429 / 503 / …
err = json.loads(e.response["OriginalMessage"])["error"]
if status == 400:
raise # bad request — fix it, never retry
elif err["code"] == "service_overloaded":
backoff(err.get("retry_after", 1)) # 429 or 503 — back off with jitter
else:
retry_once() # 5xx — retry once, then report err["request_id"]

Error envelope

Every error body is JSON with the same shape:

{ "error": { "code": "", "message": "", "request_id": "" } }

Optional fields extend it: detail_code (a specific reason), retryable (bool), and retry_after (seconds). Sarvam Vision always carries detail_code and retryable.

Overload (backpressure) sheds immediately — it does not queue

At capacity, the container rejects excess work at once rather than queueing it, using error.code = "service_overloaded" with a body retry_after:

ModelOriginalStatusCodeerror.codeRetry
Speech-to-Text (Saaras v3)429service_overloadedback off; body retry_after seconds
Text-to-Speech (Bulbul v3)503service_overloadedback off; body retry_after seconds
Sarvam Vision (OCR)503service_overloadedback off; body retry_after seconds

Client rule: treat 429 or 503 with code: "service_overloaded" as “retry after retry_after seconds, with jitter.” Never retry a 400 — fix the request instead.