Error Handling
The Ashr Labs SDK provides specific exception classes for different error scenarios, making it easy to handle errors gracefully.
Exception Hierarchy
AshrLabsError (base)
├── AuthenticationError (401)
├── AuthorizationError (403)
├── NotFoundError (404)
├── ValidationError (422)
├── RateLimitError (429)
└── ServerError (5xx)
Exception Classes
AshrLabsError
Base exception for all SDK errors.
Attributes:
message(str): Error messagestatus_code(int | None): HTTP status code if applicableresponse(dict | None): Raw response body if available
from ashr_labs import AshrLabsError
try:
result = client.get_dataset(dataset_id=42)
except AshrLabsError as e:
print(f"Error: {e.message}")
print(f"Status Code: {e.status_code}")
print(f"Response: {e.response}")
AuthenticationError
Raised when API key authentication fails (HTTP 401).
Common causes:
- Invalid API key
- Expired API key
- Revoked API key
- Malformed Authorization header
from ashr_labs import AuthenticationError
try:
datasets = client.list_datasets(tenant_id=1)
except AuthenticationError as e:
print("Authentication failed!")
print("Please check your API key is valid and not expired.")
# Log the user out or prompt for new credentials
AuthorizationError
Raised when the API key lacks permission (HTTP 403).
Common causes:
- Trying to access a resource in a different tenant
- Using API key for OAuth-only endpoints
- Insufficient scopes on the API key
from ashr_labs import AuthorizationError
try:
# Attempting to access another tenant's data
dataset = client.get_dataset(dataset_id=999)
except AuthorizationError as e:
print("Access denied!")
print("You don't have permission to access this resource.")
NotFoundError
Raised when a requested resource doesn't exist (HTTP 404).
Common causes:
- Invalid resource ID
- Resource was deleted
- Typo in the ID
from ashr_labs import NotFoundError
try:
dataset = client.get_dataset(dataset_id=99999)
except NotFoundError as e:
print(f"Dataset not found: {e.message}")
# Handle missing resource gracefully
ValidationError
Raised when request validation fails (HTTP 422).
Common causes:
- Missing required fields
- Invalid field types
- Schema validation failure
- Invalid parameter values
from ashr_labs import ValidationError
try:
run = client.create_run(
tenant_id=1,
dataset_id=-1, # Invalid ID
result={}
)
except ValidationError as e:
print(f"Validation failed: {e.message}")
# Show user which fields need correction
RateLimitError
Raised when rate limits are exceeded (HTTP 429).
Common causes:
- Too many requests in a short period
- Burst limit exceeded
import time
from ashr_labs import RateLimitError
def fetch_with_retry(client, dataset_id, max_retries=3):
for attempt in range(max_retries):
try:
return client.get_dataset(dataset_id=dataset_id)
except RateLimitError:
if attempt < max_retries - 1:
wait_time = 2 ** attempt # Exponential backoff
print(f"Rate limited. Waiting {wait_time}s...")
time.sleep(wait_time)
else:
raise
ServerError
Raised when the server encounters an internal error (HTTP 5xx).
Common causes:
- Server maintenance
- Temporary outage
- Internal service failure
from ashr_labs import ServerError
try:
datasets = client.list_datasets(tenant_id=1)
except ServerError as e:
print("Server error occurred. Please try again later.")
# Log the error for monitoring
log_error(f"Server error: {e.status_code} - {e.message}")
Best Practices
1. Catch Specific Exceptions
Always catch the most specific exception first:
from ashr_labs import (
AshrLabsClient,
AuthenticationError,
AuthorizationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
AshrLabsError,
)
try:
dataset = client.get_dataset(dataset_id=42)
except AuthenticationError:
# Handle auth failure - maybe refresh credentials
handle_auth_failure()
except AuthorizationError:
# Handle permission denied
show_permission_error()
except NotFoundError:
# Handle missing resource
show_not_found_message()
except ValidationError as e:
# Handle validation errors
show_validation_errors(e.response)
except RateLimitError:
# Handle rate limiting
schedule_retry()
except ServerError:
# Handle server errors
show_temporary_error()
except AshrLabsError as e:
# Catch-all for other API errors
log_unexpected_error(e)
2. Implement Retry Logic
For transient errors, implement retry with exponential backoff:
import time
from ashr_labs import RateLimitError, ServerError
def robust_request(func, *args, max_retries=3, **kwargs):
"""Execute a request with automatic retry for transient errors."""
last_error = None
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except (RateLimitError, ServerError) as e:
last_error = e
if attempt < max_retries - 1:
wait_time = (2 ** attempt) + (random.random() * 0.1)
time.sleep(wait_time)
continue
except AshrLabsError:
# Don't retry auth/validation/not-found errors
raise
raise last_error
# Usage
dataset = robust_request(client.get_dataset, dataset_id=42)
3. Log Errors Appropriately
import logging
logger = logging.getLogger(__name__)
try:
result = client.create_run(...)
except AuthenticationError as e:
logger.error(f"Auth failed: {e.message}")
raise
except ValidationError as e:
logger.warning(f"Validation error: {e.message}", extra={"response": e.response})
raise
except ServerError as e:
logger.critical(f"Server error {e.status_code}: {e.message}")
raise
4. User-Friendly Error Messages
Map technical errors to user-friendly messages:
ERROR_MESSAGES = {
AuthenticationError: "Your session has expired. Please log in again.",
AuthorizationError: "You don't have permission to perform this action.",
NotFoundError: "The requested item could not be found.",
ValidationError: "Please check your input and try again.",
RateLimitError: "Too many requests. Please wait a moment and try again.",
ServerError: "We're experiencing technical difficulties. Please try again later.",
}
def get_user_message(error):
for error_type, message in ERROR_MESSAGES.items():
if isinstance(error, error_type):
return message
return "An unexpected error occurred."
5. Context Managers for Cleanup
from contextlib import contextmanager
@contextmanager
def safe_api_operation(operation_name):
"""Context manager for safe API operations with cleanup."""
try:
yield
except AshrLabsError as e:
logger.error(f"{operation_name} failed: {e}")
# Perform any necessary cleanup
raise
# Usage
with safe_api_operation("Create Run"):
run = client.create_run(...)
Network Errors
The SDK also handles network-level errors:
from ashr_labs import AshrLabsError
try:
result = client.health_check()
except AshrLabsError as e:
if "Network error" in str(e):
print("Cannot connect to the API. Check your internet connection.")
elif "timed out" in str(e).lower():
print("Request timed out. The server may be slow or unreachable.")
else:
raise
Debugging Tips
Enable detailed logging to debug issues:
import logging
# Enable debug logging for the SDK
logging.basicConfig(level=logging.DEBUG)
# Or specifically for urllib
logging.getLogger("urllib.request").setLevel(logging.DEBUG)
Check the raw response in exceptions:
try:
result = client.get_dataset(dataset_id=42)
except AshrLabsError as e:
print(f"Status: {e.status_code}")
print(f"Message: {e.message}")
print(f"Raw response: {e.response}")