Error Handling
The Functor SDK provides a comprehensive error handling system with detailed exception types, automatic retry logic, and graceful degradation patterns for robust application development.
Exception Hierarchy
Base Exception Classes
from functor_sdk import (# Base exceptionsFunctorError, # Base exception for all SDK errorsFunctorAPIError, # Base exception for API-related errors# Authentication & AuthorizationFunctorAuthenticationError, # Invalid API key or tokenFunctorAuthorizationError, # Insufficient permissions# Client errorsFunctorValidationError, # Invalid request parametersFunctorNotFoundError, # Resource not foundFunctorTimeoutError, # Request timeoutFunctorConnectionError, # Network connection issues# Server errorsFunctorRateLimitError, # Rate limit exceededFunctorServerError, # Internal server errorFunctorServiceUnavailableError # Service temporarily unavailable)
Exception Hierarchy Tree
FunctorError├── FunctorAPIError│ ├── FunctorAuthenticationError│ ├── FunctorAuthorizationError│ ├── FunctorValidationError│ ├── FunctorNotFoundError│ ├── FunctorTimeoutError│ ├── FunctorConnectionError│ ├── FunctorRateLimitError│ ├── FunctorServerError│ └── FunctorServiceUnavailableError└── FunctorClientError # Non-API client errors
Basic Error Handling
Simple Try-Catch Pattern
from functor_sdk import FunctorClient, FunctorAPIErrorclient = FunctorClient()try:result = client.queries.execute("What is machine learning?")print(result.answer)except FunctorAPIError as e:print(f"API Error {e.status_code}: {e.message}")except Exception as e:print(f"Unexpected error: {e}")
Specific Exception Handling
from functor_sdk import (FunctorClient,FunctorAuthenticationError,FunctorNotFoundError,FunctorTimeoutError,FunctorConnectionError)client = FunctorClient()try:result = client.queries.execute("What is AI?")print(result.answer)except FunctorAuthenticationError:print("Authentication failed - check your API key")except FunctorNotFoundError as e:print(f"Resource not found: {e.message}")except FunctorTimeoutError:print("Request timed out - try again")except FunctorConnectionError:print("Connection failed - check your network")except Exception as e:print(f"Unexpected error: {e}")
Advanced Error Handling Patterns
Retry Logic with Exponential Backoff
import timefrom functor_sdk import (FunctorClient,FunctorAPIError,FunctorConnectionError,FunctorTimeoutError,FunctorRateLimitError)def execute_with_retry(client, operation, max_retries=3):for attempt in range(max_retries):try:return operation()except FunctorRateLimitError as e:if attempt < max_retries - 1:# Wait longer for rate limitwait_time = 2 ** (attempt + 2) # 4, 8, 16 secondsprint(f"Rate limited, waiting {wait_time} seconds...")time.sleep(wait_time)else:raiseexcept (FunctorConnectionError, FunctorTimeoutError) as e:if attempt < max_retries - 1:# Exponential backoff for connection issueswait_time = 2 ** attempt # 1, 2, 4 secondsprint(f"Connection error, retrying in {wait_time} seconds...")time.sleep(wait_time)else:raiseexcept FunctorAPIError as e:# Don't retry for client errors (4xx)if 400 <= e.status_code < 500:raise# Retry for server errors (5xx)elif attempt < max_retries - 1:wait_time = 2 ** attemptprint(f"Server error, retrying in {wait_time} seconds...")time.sleep(wait_time)else:raise# Usageclient = FunctorClient()def query_operation():return client.queries.execute("What is machine learning?")try:result = execute_with_retry(client, query_operation)print(result.answer)except Exception as e:print(f"Operation failed after retries: {e}")
Circuit Breaker Pattern
import timefrom enum import Enumfrom functor_sdk import FunctorClient, FunctorAPIErrorclass CircuitState(Enum):CLOSED = "closed" # Normal operationOPEN = "open" # Circuit is open, failing fastHALF_OPEN = "half_open" # Testing if service is backclass CircuitBreaker:def __init__(self, failure_threshold=5, timeout=60):self.failure_threshold = failure_thresholdself.timeout = timeoutself.failure_count = 0self.last_failure_time = Noneself.state = CircuitState.CLOSEDdef call(self, operation):if self.state == CircuitState.OPEN:if time.time() - self.last_failure_time > self.timeout:self.state = CircuitState.HALF_OPENelse:raise Exception("Circuit breaker is OPEN")try:result = operation()self.on_success()return resultexcept FunctorAPIError as e:self.on_failure()raisedef on_success(self):self.failure_count = 0self.state = CircuitState.CLOSEDdef on_failure(self):self.failure_count += 1self.last_failure_time = time.time()if self.failure_count >= self.failure_threshold:self.state = CircuitState.OPEN# Usageclient = FunctorClient()circuit_breaker = CircuitBreaker(failure_threshold=3, timeout=30)def query_operation():return client.queries.execute("What is AI?")try:result = circuit_breaker.call(query_operation)print(result.answer)except Exception as e:print(f"Circuit breaker error: {e}")
Graceful Degradation
from functor_sdk import (FunctorClient,FunctorAPIError,FunctorConnectionError,FunctorTimeoutError)class GracefulClient:def __init__(self, client):self.client = clientself.cache = {} # Simple in-memory cachedef execute_query_with_fallback(self, query):# Try primary operationtry:result = self.client.queries.execute(query)# Cache successful resultself.cache[query] = resultreturn resultexcept FunctorConnectionError:print("Connection failed, trying cached result...")return self._get_cached_result(query)except FunctorTimeoutError:print("Request timed out, trying cached result...")return self._get_cached_result(query)except FunctorAPIError as e:if e.status_code >= 500: # Server errorprint("Server error, trying cached result...")return self._get_cached_result(query)else: # Client errorraisedef _get_cached_result(self, query):if query in self.cache:cached_result = self.cache[query]print(f"Using cached result for: {query}")return cached_resultelse:raise Exception("No cached result available and service is unavailable")# Usageclient = FunctorClient()graceful_client = GracefulClient(client)try:result = graceful_client.execute_query_with_fallback("What is machine learning?")print(result.answer)except Exception as e:print(f"All fallback options failed: {e}")
Error Context and Debugging
Detailed Error Information
from functor_sdk import FunctorClient, FunctorAPIErrorimport tracebackclient = FunctorClient()try:result = client.queries.execute("What is AI?")except FunctorAPIError as e:print("=== API Error Details ===")print(f"Error Type: {type(e).__name__}")print(f"Status Code: {e.status_code}")print(f"Message: {e.message}")print(f"Request URL: {e.request_url}")print(f"Request Method: {e.request_method}")if hasattr(e, 'response_body'):print(f"Response Body: {e.response_body}")if hasattr(e, 'request_body'):print(f"Request Body: {e.request_body}")print("\n=== Stack Trace ===")traceback.print_exc()except Exception as e:print(f"Unexpected error: {e}")print("\n=== Stack Trace ===")traceback.print_exc()
Error Logging
import loggingfrom functor_sdk import FunctorClient, FunctorAPIError# Configure logginglogging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')logger = logging.getLogger(__name__)class LoggingClient:def __init__(self, client):self.client = clientdef execute_query_with_logging(self, query):logger.info(f"Executing query: {query}")try:result = self.client.queries.execute(query)logger.info(f"Query successful: {len(result.answer)} characters")return resultexcept FunctorAPIError as e:logger.error(f"API Error {e.status_code}: {e.message}")logger.error(f"Request URL: {e.request_url}")raiseexcept Exception as e:logger.error(f"Unexpected error: {e}")raise# Usageclient = FunctorClient()logging_client = LoggingClient(client)try:result = logging_client.execute_query_with_logging("What is machine learning?")print(result.answer)except Exception as e:print(f"Query failed: {e}")
Error Recovery Strategies
Automatic Recovery
from functor_sdk import (FunctorClient,FunctorAPIError,FunctorConnectionError,FunctorTimeoutError,FunctorRateLimitError)class ResilientClient:def __init__(self, client):self.client = clientself.retry_config = {FunctorConnectionError: {"max_retries": 3, "backoff": "exponential"},FunctorTimeoutError: {"max_retries": 2, "backoff": "linear"},FunctorRateLimitError: {"max_retries": 5, "backoff": "exponential"},}def execute_with_recovery(self, operation, *args, **kwargs):last_exception = Nonefor attempt in range(5): # Maximum attemptstry:return operation(*args, **kwargs)except Exception as e:last_exception = e# Check if we should retry this errorretry_config = self.retry_config.get(type(e))if not retry_config or attempt >= retry_config["max_retries"]:break# Calculate wait timeif retry_config["backoff"] == "exponential":wait_time = 2 ** attemptelse: # linearwait_time = attempt + 1print(f"Attempt {attempt + 1} failed: {e}")print(f"Retrying in {wait_time} seconds...")time.sleep(wait_time)# All retries failedraise last_exception# Usageclient = FunctorClient()resilient_client = ResilientClient(client)try:result = resilient_client.execute_with_recovery(client.queries.execute,"What is machine learning?")print(result.answer)except Exception as e:print(f"Operation failed after all retries: {e}")
Health Check Integration
from functor_sdk import FunctorClient, FunctorAPIErrorclass HealthAwareClient:def __init__(self, client):self.client = clientself.last_health_check = 0self.health_check_interval = 300 # 5 minutesdef execute_with_health_check(self, operation, *args, **kwargs):# Check if we need to verify system healthcurrent_time = time.time()if current_time - self.last_health_check > self.health_check_interval:if not self._check_system_health():raise Exception("System health check failed")self.last_health_check = current_time# Execute operationtry:return operation(*args, **kwargs)except FunctorAPIError as e:# If we get a server error, check system healthif e.status_code >= 500:if not self._check_system_health():raise Exception("System appears to be down")raisedef _check_system_health(self):try:health = self.client.health.check()return health["status"] == "healthy"except Exception:return False# Usageclient = FunctorClient()health_client = HealthAwareClient(client)try:result = health_client.execute_with_health_check(client.queries.execute,"What is AI?")print(result.answer)except Exception as e:print(f"Operation failed: {e}")
Best Practices
Error Handling Best Practices
- Handle specific exceptions: Catch specific exception types rather than generic ones
- Implement retry logic: For transient errors like network issues
- Use exponential backoff: Avoid overwhelming the server
- Log errors appropriately: Include context for debugging
- Implement circuit breakers: Prevent cascading failures
- Provide fallback mechanisms: Graceful degradation when possible
- Monitor error rates: Track and alert on error patterns
Production Error Handling
from functor_sdk import FunctorClient, FunctorAPIErrorimport loggingimport timefrom typing import Optional, Callable, Anyclass ProductionErrorHandler:def __init__(self, client: FunctorClient):self.client = clientself.logger = logging.getLogger(__name__)self.error_counts = {}self.max_errors_per_minute = 10def execute_with_production_handling(self,operation: Callable,operation_name: str,*args,**kwargs) -> Optional[Any]:"""Execute operation with production-grade error handling"""# Check error rateif self._is_error_rate_too_high():self.logger.warning(f"Error rate too high, skipping {operation_name}")return Nonetry:result = operation(*args, **kwargs)self._reset_error_count(operation_name)return resultexcept FunctorAPIError as e:self._handle_api_error(e, operation_name)return Noneexcept Exception as e:self._handle_unexpected_error(e, operation_name)return Nonedef _is_error_rate_too_high(self) -> bool:current_minute = int(time.time() / 60)total_errors = sum(count for minute, count in self.error_counts.items()if minute >= current_minute - 1)return total_errors > self.max_errors_per_minutedef _handle_api_error(self, error: FunctorAPIError, operation_name: str):self._increment_error_count(operation_name)if error.status_code >= 500:self.logger.error(f"Server error in {operation_name}: {error.message}")elif error.status_code == 429:self.logger.warning(f"Rate limited in {operation_name}")elif error.status_code >= 400:self.logger.warning(f"Client error in {operation_name}: {error.message}")def _handle_unexpected_error(self, error: Exception, operation_name: str):self._increment_error_count(operation_name)self.logger.error(f"Unexpected error in {operation_name}: {error}")def _increment_error_count(self, operation_name: str):current_minute = int(time.time() / 60)key = f"{operation_name}_{current_minute}"self.error_counts[key] = self.error_counts.get(key, 0) + 1def _reset_error_count(self, operation_name: str):# Clean up old error countscurrent_minute = int(time.time() / 60)self.error_counts = {k: v for k, v in self.error_counts.items()if int(k.split('_')[-1]) >= current_minute - 1}# Usageclient = FunctorClient()error_handler = ProductionErrorHandler(client)result = error_handler.execute_with_production_handling(client.queries.execute,"query_execution","What is machine learning?")if result:print(result.answer)else:print("Query failed, check logs for details")
Next Steps
- Async vs Sync - Choose the right interface for error handling
- Client Reference - Configure client for robust error handling
- Health Namespace - Monitor system health
- Queries Namespace - Apply error handling to queries