Error Handling Model
The Saner Python SDK provides a structured exception handling system designed for reliable automation, predictable integrations, and faster debugging. Instead of returning ambiguous failures, the SDK raises clear, categorized exceptions that help developers immediately identify the cause of an issue.
Errors typically fall into four categories:
- validation errors (client-side)
- authentication errors
- API response errors
- retry and request failures, including rate limiting
All SDK exceptions inherit from a single base exception class, making them easy to manage in both simple scripts and enterprise workflows. Every exception also carries structured context (status code, response body, method, URL, attempt count) so you can branch on it programmatically instead of parsing the message string.
Note: Error handling behaves identically across all three client classes (
SanerClient,PlatformClient,CvemClient); the examples below use flat resource access (client.Organization...,client.Patch...) as ifclientis aPlatformClient/CvemClientscoped to that resource's product, since that's the shortest form. On aSanerClientinstance, prefix with the namespace instead:client.platform.Organization...,client.cvem.Patch.... Everything else about the exception shown is unchanged. See the "Client Types" guide for the full comparison.
Exception Hierarchy
The SDK defines a clean exception structure:
SanerError
├── SanerAuthError
├── SanerRequestError
│ └── SanerRateLimitError
└── SanerResponseErrorThis allows developers to:
- catch all SDK errors together
- handle authentication failures separately
- detect rate limiting precisely, distinct from other network issues
- detect retry/network issues precisely
- identify malformed API responses quickly
Example:
from saner.exceptions import SanerError
try:
response = client.Organization.get(organization="Example")
except SanerError as e:
print(e)Structured Exception Attributes
Every SanerError (and its subclasses) carries the following attributes, populated whenever the information is available:
| Attribute | Type | Description |
|---|---|---|
message | str | Human-readable description of the failure |
status_code | int | None | HTTP status code, if a response was received |
response_body | str | None | Response body, redacted the same way logs are and truncated for very large bodies |
method | str | None | HTTP method or RPC method name involved |
url | str | None | Fully-qualified request URL |
attempts | int | None | Number of attempts actually made before the error was raised |
Because response_body goes through the same redaction as the SDK's own logs, it's safe to forward a caught exception's fields into your own logging or APM stack (Sentry, Datadog, etc.) without re-checking it for credentials first. A clean body is passed through byte-for-byte, so this doesn't affect debugging output that never had a secret in it.
This means production error handlers can act on structured data instead of parsing text:
from saner.exceptions import SanerRequestError
try:
response = client.Patch.getRemediationJobStatus(accountid="Default", name="job1")
except SanerRequestError as e:
print("status:", e.status_code)
print("attempts:", e.attempts)
print("endpoint:", e.method, e.url)Validation Errors (Client-Side)
Before sending a request, the SDK validates parameters locally using its validation engine.
Validation errors occur when:
- required parameters are missing
- incorrect parameter types are provided
- enum values are invalid
- formats (email, IP, MAC, date) are incorrect
- password strength requirements fail
- list/dict structures are invalid
Example:
response = client.Organization.get(organization=123)Error:
TypeError: organization must be stringThese errors stop execution immediately and prevent invalid requests from reaching the server.
Authentication Errors
Authentication failures occur when API credentials are invalid or permissions are insufficient.
Raised exception:
SanerAuthErrorTriggered by:
- HTTP 401 (Unauthorized)
- HTTP 403 (Forbidden)
Example:
from saner.exceptions import SanerAuthError
try:
response = client.Organization.get(organization="Example")
except SanerAuthError as e:
print("Authentication failed:", e)Authentication errors are never retried automatically because credentials must be corrected manually.
Rate Limit Errors
Rate limit failures occur when the Saner API has throttled your account due to too many requests.
Raised exception:
SanerRateLimitErrorTriggered by:
- HTTP 429 (Too Many Requests)
Unlike other failures, HTTP 429 responses are automatically retried by the SDK before this exception is ever raised, up to max_retries times, honoring the server's Retry-After header when present (capped at 60 seconds per attempt). SanerRateLimitError is only raised once retries are exhausted.
It carries one extra attribute beyond the base exception:
| Attribute | Type | Description |
|---|---|---|
retry_after | float | None | Seconds the server asked you to wait, parsed from Retry-After, if provided |
Example:
from saner.exceptions import SanerRateLimitError
try:
response = client.Vulnerability.getAssets()
except SanerRateLimitError as e:
print("Rate limited. Retry after:", e.retry_after, "seconds")SanerRateLimitError is a subclass of SanerRequestError, so existing code that catches SanerRequestError will continue to catch rate-limit failures without any changes.
See the "Retry and Timeout Behaviour" guide for the full retry policy.
API Response Errors
Response errors occur when the server returns a response that cannot be parsed correctly.
Raised exception:
SanerResponseErrorTypical causes:
- invalid JSON returned by server
- unexpected response structure
- corrupted response payload
- the response body exceeded the client's
max_response_byteslimit (see the "Client Configuration" guide), raised before or immediately after the oversized body would have been fully read, so it never sits in memory
Example:
from saner.exceptions import SanerResponseError
try:
response = client.Organization.get(organization="Example")
except SanerResponseError as e:
print("Invalid response received:", e)These errors indicate a server formatting issue rather than a connectivity failure.
Retry Failures and Request Errors
Request-level failures occur when communication with the server fails even after automatic retries.
Raised exception:
SanerRequestErrorTriggered by:
- connection failures
- request timeouts
- SSL verification failures
- retry exhaustion after HTTP 500 / 502 / 503 / 504 responses
- unexpected transport-level errors
Example:
from saner.exceptions import SanerRequestError
try:
response = client.Organization.get(organization="Example")
except SanerRequestError as e:
print("Request failed:", e)Note: Not every failing request is retried the same number of times. Read-only and other idempotent calls are always retried on a transient failure; calls that create or modify data are only retried when they are known not to have reached the server, or when the client is configured with
retry_on_write=True. See the "Retry and Timeout Behaviour" guide for the full policy. This affects how many attempts you'll see beforeSanerRequestErroris raised.
Example: Basic Error Handling Pattern
For lightweight scripts:
try:
response = client.Organization.get(organization="Example")
except Exception as e:
print(e)This captures all SDK errors and validation failures.
Example: Production-Grade Error Handling Pattern
For automation pipelines and integrations at scale:
from saner.exceptions import (
SanerAuthError,
SanerRateLimitError,
SanerRequestError,
SanerResponseError,
)
try:
response = client.Organization.get(organization="Example")
except SanerAuthError:
print("Check API credentials or permissions")
except SanerRateLimitError as e:
print(f"Rate limited, back off for {e.retry_after or 'a while'} seconds")
except SanerRequestError as e:
print(f"Network issue or retry limit reached (status={e.status_code}, attempts={e.attempts})")
except SanerResponseError:
print("Unexpected server response format")Note:
SanerRateLimitErrormust be caught beforeSanerRequestErrorin anexceptchain, since it is a subclass of it. Python matches the first applicableexceptclause in order.
This enables smarter recovery strategies depending on failure type.
Example Error Messages Returned by the SDK
The SDK produces clear, human-readable error messages such as:
organization must be string
organization cannot be empty
email must be a valid email
password must contain a special character
Authentication failed (HTTP 401)
Rate limited (HTTP 429) after 4 attempts: ...
HTTP error 503 after 4 attempts
Timeout after 4 attempts
SSL error: certificate verify failedNote: The attempt count in these messages reflects attempts actually made, not always
max_retries + 1. A write operation that isn't eligible for retry (see the "Retry and Timeout Behaviour" guide) can fail after a single attempt even withmax_retries=3configured.
These messages help developers quickly identify integration mistakes without inspecting raw HTTP responses.
Benefits for Automation and Production Workflows
The structured exception system helps developers:
- detect failures instantly
- distinguish validation vs authentication vs rate-limiting vs network issues
- implement retry-aware automation logic
- reduce debugging time
- build resilient multi-environment integrations
- maintain predictable SDK behavior at scale
This makes the Saner Python SDK well-suited for both simple scripts and enterprise automation pipelines.
Updated 5 days ago
