Handle errors gracefully with retries, fallbacks, and proper exception handling.
Prerequisites
An Auriko API key
Python 3.10+ with auriko SDK installed (pip install auriko)
OR Node.js 18+ with @auriko/sdk installed (npm install @auriko/sdk)
Error types
All Auriko errors extend AurikoAPIError with these fields:
Field Type Description messagestrHuman-readable error message status_codeintHTTP status code codestrMachine-readable error code typestr | NoneError category paramstr | NoneParameter that caused the error response_headersResponseHeadersResponse headers (includes request_id for support)
The SDK provides 10 specific error classes:
Exception Status When AuthenticationError401 Invalid or missing API key InvalidRequestError400 Malformed request, invalid parameter value, or missing required parameter InsufficientCreditsError402 Account has insufficient credits BudgetExceededError402 Budget limit hit (workspace, key, or Bring Your Own Key (BYOK) scope) ModelNotFoundError404 Requested model not in catalog RateLimitError429 Rate limit exceeded InternalError500 Unexpected Auriko server error ProviderError502/503/504 Upstream provider error, timeout, or all providers failed ProviderAuthError401 BYOK key authentication failed at provider ServiceUnavailableError503 Auriko service temporarily unavailable
Some error codes (like missing_required_parameter and no_providers_available) map to shared classes (InvalidRequestError and ProviderError respectively). Any unrecognized error falls to the AurikoAPIError base class via status-code fallback.
See the Python SDK Reference or TypeScript SDK Reference for complete error class fields and hierarchy.
Handle errors
Catch typed exceptions:
import os
from auriko import (
Client,
AurikoAPIError,
AuthenticationError,
RateLimitError,
BudgetExceededError,
ModelNotFoundError,
ProviderError,
# Also available: InvalidRequestError, InsufficientCreditsError,
# InternalError, ProviderAuthError, ServiceUnavailableError
)
client = Client(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1"
)
try :
response = client.chat.completions.create(
model = "gpt-4o" ,
messages = [{ "role" : "user" , "content" : "Hello!" }]
)
print (response.choices[ 0 ].message.content)
except AuthenticationError as e:
print ( f "Check your API key: { e } " )
except RateLimitError as e:
print ( f "Rate limited, retry later: { e } " )
except BudgetExceededError as e:
print ( f "Budget exceeded: { e } " )
except ModelNotFoundError as e:
print ( f "Model not found: { e } " )
except ProviderError as e:
print ( f "Provider error: { e } " )
except AurikoAPIError as e:
# Catches all other Auriko errors (InternalError,
# ServiceUnavailableError, InvalidRequestError, etc.)
print ( f "API error ( { e.status_code } ): { e } " )
Use built-in retries
The SDK automatically retries transient errors with exponential backoff:
Setting Value Max retries 2 (default) Initial interval 500ms Max interval 30 seconds Backoff Exponential (1.5 exponent) + random jitter Retried status codes 429, 500, 502, 503, 504 Connection/timeout errors Retried Retry-After headerRespected (overrides backoff when present)
import os
from auriko import Client
# Default: 2 retries with exponential backoff
client = Client(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1"
)
# More retries for resilience
client = Client(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1" ,
max_retries = 5
)
# Disable retries entirely
client = Client(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1" ,
max_retries = 0
)
When the server returns a Retry-After header (common with 429 responses), the SDK uses that value instead of the calculated backoff interval.
Retry manually
For more control, implement custom retry logic:
import os
import time
from auriko import Client, RateLimitError, ProviderError
client = Client(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1" ,
max_retries = 0 # Disable auto-retry
)
def make_request_with_retry ( messages , max_retries = 3 ):
last_error = None
for attempt in range (max_retries):
try :
return client.chat.completions.create(
model = "gpt-4o" ,
messages = messages
)
except RateLimitError as e:
last_error = e
wait_time = min ( 2 ** attempt, 60 ) # Cap at 60 seconds
print ( f "Rate limited, waiting { wait_time } s..." )
time.sleep(wait_time)
except ProviderError as e:
last_error = e
wait_time = 2 ** attempt
print ( f "Provider error, retrying in { wait_time } s..." )
time.sleep(wait_time)
raise last_error
# Usage
response = make_request_with_retry([{ "role" : "user" , "content" : "Hello!" }])
Retry asynchronously
Retry with async/await:
import os
import asyncio
from auriko import AsyncClient, RateLimitError, ProviderError
client = AsyncClient(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1" ,
max_retries = 0
)
async def make_request_with_backoff ( messages , max_retries = 3 ):
for attempt in range (max_retries):
try :
return await client.chat.completions.create(
model = "gpt-4o" ,
messages = messages
)
except (RateLimitError, ProviderError) as e:
if attempt == max_retries - 1 :
raise
wait_time = 2 ** attempt
await asyncio.sleep(wait_time)
Side effects and retries: When using tools or multi-step workflows, consider whether retries are safe. A retried request that triggers a tool call may execute the tool twice. For idempotency-sensitive operations, either disable automatic retries (max_retries=0) or implement your own deduplication logic.
Fall back to another model
Use a cheaper/faster model as fallback:
import os
from auriko import Client, AurikoAPIError
client = Client(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1"
)
def chat_with_fallback ( messages ):
try :
# Try primary model
return client.chat.completions.create(
model = "gpt-4o" ,
messages = messages,
routing = { "max_ttft_ms" : 200 }
)
except AurikoAPIError as e:
print ( f "Primary failed ( { e } ), trying fallback..." )
# Fallback to a different model
return client.chat.completions.create(
model = "gpt-4o-mini" ,
messages = messages
)
Use circuit breakers
Prevent cascading failures:
import os
from datetime import datetime, timedelta, timezone
from auriko import Client, ProviderError
client = Client(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1"
)
class CircuitBreaker :
def __init__ ( self , failure_threshold = 5 , reset_timeout = 60 ):
self .failures = 0
self .failure_threshold = failure_threshold
self .reset_timeout = reset_timeout
self .last_failure = None
self .is_open = False
def record_failure ( self ):
self .failures += 1
self .last_failure = datetime.now(timezone.utc)
if self .failures >= self .failure_threshold:
self .is_open = True
def record_success ( self ):
self .failures = 0
self .is_open = False
def can_proceed ( self ):
if not self .is_open:
return True
if datetime.now(timezone.utc) - self .last_failure > timedelta( seconds = self .reset_timeout):
self .is_open = False
return True
return False
# Usage
breaker = CircuitBreaker()
def safe_request ( messages ):
if not breaker.can_proceed():
raise Exception ( "Circuit breaker open, try later" )
try :
response = client.chat.completions.create(
model = "gpt-4o" ,
messages = messages
)
breaker.record_success()
return response
except ProviderError as e:
breaker.record_failure()
raise
Set timeouts
import os
import httpx
from auriko import Client
client = Client(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1" ,
timeout = 30.0 # 30 second timeout
)
try :
response = client.chat.completions.create(
model = "gpt-4o" ,
messages = [{ "role" : "user" , "content" : "Write a long essay..." }]
)
except httpx.TimeoutException:
print ( "Request timed out" )
Log errors
Log errors for debugging:
import os
import logging
from auriko import Client, AurikoAPIError
logging.basicConfig( level = logging. INFO )
logger = logging.getLogger( __name__ )
client = Client(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1"
)
try :
response = client.chat.completions.create(
model = "gpt-4o" ,
messages = [{ "role" : "user" , "content" : "Hello!" }]
)
except AurikoAPIError as e:
logger.exception( "Chat completion failed" , extra = {
"error_type" : type (e). __name__ ,
"status_code" : e.status_code,
"request_id" : e.response_headers.request_id,
"model" : "gpt-4o" ,
})
raise
Map OpenAI SDK errors
If you use the OpenAI SDK directly (with base_url pointed at Auriko), you can convert OpenAI errors to typed Auriko errors using map_openai_error():
import os
import openai
from auriko import map_openai_error, RateLimitError, BudgetExceededError
client = openai.OpenAI(
api_key = os.environ[ "AURIKO_API_KEY" ],
base_url = "https://api.auriko.ai/v1"
)
try :
response = client.chat.completions.create(
model = "gpt-4o" ,
messages = [{ "role" : "user" , "content" : "Hello!" }]
)
except openai.APIStatusError as e:
auriko_error = map_openai_error(e)
if isinstance (auriko_error, RateLimitError):
print ( f "Rate limited. Retry after: { auriko_error.response_headers.rate_limit_reset } " )
elif isinstance (auriko_error, BudgetExceededError):
print ( f "Budget exceeded: { auriko_error.message } " )
else :
raise auriko_error
This gives you access to typed error fields (status_code, code, response_headers) and fine-grained isinstance checks, even when using the OpenAI client.
map_openai_error() is Python-only. TypeScript users should use the Auriko SDK directly for typed errors.
See Switching from OpenAI for migration-focused error mapping.
Best practices
Use built-in retries Let the SDK handle transient errors automatically
Log errors Log errors with context for debugging
Set timeouts Prevent requests from hanging indefinitely
Have fallbacks Use fallback models for critical paths