Skip to content

Exception Reference

Comprehensive error handling guide for the ReZEN Python API client, including exception types, handling patterns, and troubleshooting.


Overview

Error Handling Philosophy

The ReZEN client uses a hierarchical exception system that provides:

  • Specific Error Types: Different exceptions for different error conditions
  • Rich Context: Detailed error messages with request/response information
  • Graceful Degradation: Predictable error handling for robust applications
  • Debug Information: Full context for troubleshooting and logging

Exception Hierarchy

graph TD
    A[Exception] --> B[RezenError]
    B --> C[AuthenticationError]
    B --> D[ValidationError]
    B --> E[NotFoundError]
    B --> F[RateLimitError]
    B --> G[NetworkError]
    B --> H[APIError]
    B --> I[ConfigurationError]
    H --> J[ServerError]
    H --> K[ClientError]

Core Exception Types

Base Exception

Base exception for all ReZEN API errors

from typing import List, Dict, Any

from rezen import RezenClient
from rezen.exceptions import RezenError

client: RezenClient = RezenClient()

try:
    # Any ReZEN API operation
    result: List[Dict[str, Any]] = client.teams.search_teams()
except RezenError as e:
    print(f"ReZEN API error: {e}")
    print(f"Error details: {e.details}")

Attributes: - message: Human-readable error message - status_code: HTTP status code (if applicable) - details: Additional error context - response: Raw HTTP response (if available)

Authentication Errors

API key or authentication issues

from typing import List, Dict, Any

from rezen import RezenClient
from rezen.exceptions import AuthenticationError

try:
    client: RezenClient = RezenClient(api_key="invalid_key")
    teams: List[Dict[str, Any]] = client.teams.search_teams()
except AuthenticationError as e:
    print("Authentication failed - check your API key")
    # Handle authentication error
    # - Prompt for new API key
    # - Refresh authentication token
    # - Log security event

Common Causes: - Invalid API key - Expired authentication token - Missing API key - Insufficient permissions

Security Considerations

Never log API keys in error messages. Always sanitize authentication errors before logging.

Validation Errors

Invalid input data or parameters

from typing import Dict, Any

from rezen import RezenClient
from rezen.exceptions import ValidationError

client: RezenClient = RezenClient()

try:
    # Invalid email format
    response: Dict[str, Any] = client.agents.get_agents_by_email("invalid-email")
except ValidationError as e:
    print(f"Validation error: {e.message}")
    print(f"Invalid fields: {e.invalid_fields}")
    # Handle validation error
    # - Show user-friendly error message
    # - Highlight invalid form fields
    # - Provide correction suggestions

Extended Attributes: - invalid_fields: List of fields that failed validation - validation_details: Detailed validation error information

Resource Errors

Requested resource does not exist

from typing import Dict, Any

from rezen import RezenClient
from rezen.exceptions import NotFoundError

client: RezenClient = RezenClient()

try:
    transaction: Dict[str, Any] = client.transactions.get_transaction("invalid-id")
except NotFoundError as e:
    print(f"Resource not found: {e.resource_type}")
    print(f"Resource ID: {e.resource_id}")
    # Handle not found error
    # - Show "not found" message to user
    # - Redirect to search page
    # - Log potential data inconsistency

Extended Attributes: - resource_type: Type of resource that wasn't found - resource_id: ID of the requested resource

Rate Limiting

API rate limit exceeded

import time
from typing import List, Dict, Any

from rezen import RezenClient
from rezen.exceptions import RateLimitError

client: RezenClient = RezenClient()

def api_call_with_retry() -> List[Dict[str, Any]]:
    """Make API call with rate limit retry logic.

    Returns:
        List of teams from API

    Raises:
        RateLimitError: If max retries exceeded
    """
    max_retries: int = 3
    for attempt in range(max_retries):
        try:
            return client.teams.search_teams()
        except RateLimitError as e:
            if attempt == max_retries - 1:
                raise  # Re-raise if max retries exceeded

            # Wait before retrying
            wait_time: int = e.retry_after or 60
            print(f"Rate limited. Waiting {wait_time} seconds...")
            time.sleep(wait_time)

Extended Attributes: - retry_after: Seconds to wait before retrying - limit_type: Type of rate limit (per minute, per hour, etc.) - current_usage: Current API usage count

Network Errors

Connection and network-related issues

import logging
from typing import Dict, Any, List

from rezen import RezenClient
from rezen.exceptions import NetworkError

client: RezenClient = RezenClient()

def robust_api_call() -> Dict[str, Any]:
    """Make robust API call with network error handling.

    Returns:
        API response or error dictionary
    """
    try:
        return client.agents.search_active_agents()
    except NetworkError as e:
        logging.error(f"Network error: {e}")

        # Handle different network scenarios
        if "timeout" in str(e).lower():
            # Handle timeout
            return {"error": "Request timed out. Please try again."}
        elif "connection" in str(e).lower():
            # Handle connection error
            return {"error": "Unable to connect. Check your internet connection."}
        else:
            # Handle other network errors
            return {"error": "Network error occurred. Please try again later."}

Server Errors

API server-side errors (5xx status codes)

import logging
from typing import Dict, Any, Callable, Optional

from rezen import RezenClient
from rezen.exceptions import ServerError

client: RezenClient = RezenClient()

def handle_server_error() -> Optional[Dict[str, Any]]:
    """Handle server errors with retry logic.

    Returns:
        API response if successful, None if failed
    """
    try:
        return client.transactions.get_transaction("tx-123")
    except ServerError as e:
        # Log server error for monitoring
        logging.error(f"Server error {e.status_code}: {e.message}")

        # Implement retry logic for server errors
        if e.status_code >= 500:
            # Exponential backoff retry
            return retry_with_backoff(api_call)
        else:
            # Client error, don't retry
            raise

    return None

Auto-Generated Exception Documentation

Exception Classes Module

rezen.exceptions

Custom exceptions for the ReZEN API wrapper.

AuthenticationError(message, status_code=None, response_data=None)

Bases: RezenError

Raised when authentication fails.

InvalidFieldNameError(field_name, correct_name, additional_info='')

Bases: ValidationError

Raised when incorrect field names are used.

Initialize invalid field name error.

Parameters:

Name Type Description Default
field_name str

The incorrect field name used

required
correct_name str

The correct field name to use

required
additional_info str

Additional context about the field

''

InvalidFieldValueError(field_name, value, expected_format)

Bases: ValidationError

Raised when field values don't match expected format.

Initialize invalid field value error.

Parameters:

Name Type Description Default
field_name str

The field with invalid value

required
value Any

The invalid value provided

required
expected_format str

Description of expected format

required

NetworkError(message, status_code=None, response_data=None)

Bases: RezenError

Raised when network connection fails.

NotFoundError(message, status_code=None, response_data=None)

Bases: RezenError

Raised when a resource is not found.

RateLimitError(message, status_code=None, response_data=None)

Bases: RezenError

Raised when rate limit is exceeded.

RezenError(message, status_code=None, response_data=None)

Bases: Exception

Base exception for all ReZEN API errors.

Initialize ReZEN error.

Parameters:

Name Type Description Default
message str

Error message

required
status_code Optional[int]

HTTP status code if applicable

None
response_data Optional[Dict[str, Any]]

Response data from API if available

None

ServerError(message, status_code=None, response_data=None)

Bases: RezenError

Raised when server returns 5xx error.

TransactionSequenceError(message, required_steps=None)

Bases: ValidationError

Raised when transaction builder operations are called in wrong sequence.

Initialize transaction sequence error.

Parameters:

Name Type Description Default
message str

Error message

required
required_steps Optional[List[str]]

List of required steps in order

None

ValidationError(message, status_code=None, response_data=None)

Bases: RezenError

Raised when request validation fails.


Error Handling Patterns

Comprehensive Error Handler

import logging
import time
from typing import Optional, Dict, Any, Callable, List

from rezen import RezenClient
from rezen.exceptions import (
    RezenError, AuthenticationError, ValidationError,
    NotFoundError, RateLimitError, NetworkError, ServerError
)

class RezenAPIHandler:
    """Production-ready ReZEN API handler with comprehensive error handling."""

    def __init__(self, api_key: Optional[str] = None) -> None:
        """Initialize the API handler.

        Args:
            api_key: Optional API key for authentication
        """
        self.client: RezenClient = RezenClient(api_key=api_key)
        self.logger: logging.Logger = logging.getLogger(__name__)

    def safe_api_call(self, operation: Callable[..., Any], *args: Any, **kwargs: Any) -> Dict[str, Any]:
        """Execute API call with comprehensive error handling.

        Args:
            operation: Callable API operation to execute
            *args: Positional arguments for the operation
            **kwargs: Keyword arguments for the operation

        Returns:
            Dictionary containing success status and data or error information
        """
        max_retries: int = 3
        base_delay: int = 1

        for attempt in range(max_retries):
            try:
                result: Any = operation(*args, **kwargs)
                return {"success": True, "data": result}

            except AuthenticationError as e:
                self.logger.error("Authentication failed", exc_info=True)
                return {
                    "success": False,
                    "error_type": "authentication",
                    "message": "Invalid credentials. Please check your API key.",
                    "retry": False
                }

            except ValidationError as e:
                self.logger.warning(f"Validation error: {e.invalid_fields}")
                return {
                    "success": False,
                    "error_type": "validation",
                    "message": str(e),
                    "invalid_fields": e.invalid_fields,
                    "retry": False
                }

            except NotFoundError as e:
                return {
                    "success": False,
                    "error_type": "not_found",
                    "message": f"{e.resource_type} not found",
                    "retry": False
                }

            except RateLimitError as e:
                if attempt < max_retries - 1:
                    wait_time: int = e.retry_after or (base_delay * (2 ** attempt))
                    self.logger.info(f"Rate limited. Waiting {wait_time}s...")
                    time.sleep(wait_time)
                    continue

                return {
                    "success": False,
                    "error_type": "rate_limit",
                    "message": "Rate limit exceeded. Please try again later.",
                    "retry_after": e.retry_after
                }

            except (NetworkError, ServerError) as e:
                if attempt < max_retries - 1:
                    delay: int = base_delay * (2 ** attempt)
                    self.logger.warning(f"Network/Server error. Retrying in {delay}s...")
                    time.sleep(delay)
                    continue

                return {
                    "success": False,
                    "error_type": "network" if isinstance(e, NetworkError) else "server",
                    "message": "Service temporarily unavailable. Please try again later.",
                    "retry": True
                }

            except RezenError as e:
                self.logger.error(f"Unexpected ReZEN error: {e}", exc_info=True)
                return {
                    "success": False,
                    "error_type": "api_error",
                    "message": "An unexpected error occurred. Please contact support.",
                    "retry": False
                }

            except Exception as e:
                self.logger.error(f"Unexpected error: {e}", exc_info=True)
                return {
                    "success": False,
                    "error_type": "unknown",
                    "message": "An unexpected error occurred.",
                    "retry": False
                }

        return {
            "success": False,
            "error_type": "max_retries",
            "message": "Maximum retry attempts exceeded.",
            "retry": False
        }
from typing import List, Dict, Any

# Usage of the comprehensive error handler
handler: RezenAPIHandler = RezenAPIHandler()

# Safe team search
result: Dict[str, Any] = handler.safe_api_call(
    handler.client.teams.search_teams,
    status="ACTIVE",
    page_size=10
)

if result["success"]:
    teams: List[Dict[str, Any]] = result["data"]
    print(f"Found {len(teams)} teams")
else:
    print(f"Error: {result['message']}")
    if result.get("retry"):
        print("This error may be temporary. Consider retrying.")

Context Managers for Error Handling

import logging
from contextlib import contextmanager
from typing import Generator

from rezen.exceptions import RezenError

@contextmanager
def rezen_error_context(operation_name: str) -> Generator[None, None, None]:
    """Context manager for ReZEN API operations with logging.

    Args:
        operation_name: Name of the operation for logging

    Yields:
        None

    Raises:
        RezenError: If API operation fails
        Exception: For unexpected errors
    """
    logger: logging.Logger = logging.getLogger(__name__)
    logger.info(f"Starting {operation_name}")

    try:
        yield
        logger.info(f"Completed {operation_name}")
    except RezenError as e:
        logger.error(f"Failed {operation_name}: {e}")
        raise
    except Exception as e:
        logger.error(f"Unexpected error in {operation_name}: {e}")
        raise

# Usage
from typing import List, Dict, Any

from rezen import RezenClient

client: RezenClient = RezenClient()

with rezen_error_context("team search"):
    teams: List[Dict[str, Any]] = client.teams.search_teams(status="ACTIVE")

Debugging and Troubleshooting

Logging Configuration

Production Logging Setup

import logging

from rezen import RezenClient

# Configure logging for ReZEN operations
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('rezen_api.log'),
        logging.StreamHandler()
    ]
)

# Set ReZEN client to debug mode
client: RezenClient = RezenClient(debug=True)

Error Analysis Tools

from rezen.exceptions import RezenError

def analyze_error(error: RezenError) -> None:
    """Analyze ReZEN error for debugging.

    Args:
        error: ReZEN error to analyze
    """
    print("=== Error Analysis ===")
    print(f"Error Type: {type(error).__name__}")
    print(f"Message: {error.message}")
    print(f"Status Code: {getattr(error, 'status_code', 'N/A')}")

    if hasattr(error, 'response') and error.response:
        print(f"Request URL: {error.response.request.url}")
        print(f"Request Method: {error.response.request.method}")
        print(f"Response Headers: {dict(error.response.headers)}")

    if hasattr(error, 'details') and error.details:
        print(f"Error Details: {error.details}")

# Usage
from typing import List, Dict, Any

from rezen import RezenClient

client: RezenClient = RezenClient()

try:
    teams: List[Dict[str, Any]] = client.teams.search_teams()
except RezenError as e:
    analyze_error(e)
import time
from collections import defaultdict
from typing import Dict, List, Any, DefaultDict

from rezen.exceptions import RezenError

class ErrorMonitor:
    """Monitor and track API errors for analysis."""

    def __init__(self) -> None:
        """Initialize the error monitor."""
        self.error_counts: DefaultDict[str, int] = defaultdict(int)
        self.error_history: List[Dict[str, Any]] = []

    def record_error(self, error: RezenError) -> None:
        """Record error occurrence.

        Args:
            error: ReZEN error to record
        """
        error_type: str = type(error).__name__
        self.error_counts[error_type] += 1
        self.error_history.append({
            "timestamp": time.time(),
            "error_type": error_type,
            "message": str(error),
            "status_code": getattr(error, 'status_code', None)
        })

    def get_error_summary(self) -> Dict[str, Any]:
        """Get summary of recorded errors.

        Returns:
            Dictionary containing error summary information
        """
        return {
            "total_errors": len(self.error_history),
            "error_counts": dict(self.error_counts),
            "recent_errors": self.error_history[-10:]  # Last 10 errors
        }

# Usage
from rezen import RezenClient

monitor: ErrorMonitor = ErrorMonitor()
client: RezenClient = RezenClient()

try:
    teams: List[Dict[str, Any]] = client.teams.search_teams()
except RezenError as e:
    monitor.record_error(e)
    summary: Dict[str, Any] = monitor.get_error_summary()
    print(summary)

Best Practices

:material-shield-check: Error Prevention

Proactive Error Prevention

  • Validate Input: Check data before making API calls
  • Handle Rate Limits: Implement proper rate limiting in your application
  • Use Timeouts: Set appropriate timeouts for API calls
  • Monitor Errors: Track error patterns for proactive fixes

:material-autorenew: Retry Strategies

Exponential Backoff Implementation

import random
import time
from rezen.exceptions import RateLimitError, NetworkError, ServerError

def exponential_backoff_retry(func, max_retries=3, base_delay=1):
    """Retry function with exponential backoff and jitter."""

    for attempt in range(max_retries):
        try:
            return func()
        except (RateLimitError, NetworkError, ServerError) as e:
            if attempt == max_retries - 1:
                raise  # Last attempt, re-raise the error

            # Calculate delay with exponential backoff and jitter
            delay = base_delay * (2 ** attempt)
            jitter = random.uniform(0, delay * 0.1)  # 10% jitter
            total_delay = delay + jitter

            print(f"Attempt {attempt + 1} failed. Retrying in {total_delay:.2f}s...")
            time.sleep(total_delay)

:material-security: Security Considerations

Security Best Practices

  • Never log API keys in error messages or logs
  • Sanitize error messages before showing to users
  • Implement rate limiting to prevent abuse
  • Monitor authentication failures for security threats

Quick Reference

Exception Type Mapping

Exception HTTP Status When It Occurs Retry?
AuthenticationError 401, 403 Invalid API key, insufficient permissions No
ValidationError 400 Invalid input parameters No
NotFoundError 404 Resource doesn't exist No
RateLimitError 429 Rate limit exceeded Yes (with delay)
NetworkError - Connection issues, timeouts Yes
ServerError 500-599 Server-side errors Yes
ClientError 400-499 Client-side errors (except above) No

Common Error Codes

Status Code Exception Description
400 ValidationError Bad request, invalid parameters
401 AuthenticationError Unauthorized, invalid API key
403 AuthenticationError Forbidden, insufficient permissions
404 NotFoundError Resource not found
429 RateLimitError Too many requests
500 ServerError Internal server error
502 ServerError Bad gateway
503 ServerError Service unavailable

Next Steps