Skip to content

Base Exceptions API

Core exception classes and structured error handling.

Base Exception Class

JPYBaseException

JPYBaseException

Bases: Exception

Base exception class for all jinpy-utils exceptions.

This exception provides structured error information using Pydantic models and supports JSON serialization for better error handling and logging.

Parameters:

Name Type Description Default
error_code str

Unique identifier for the error type

required
message str

Human-readable error description

required
details dict[str, Any] | None

Additional structured error information

None
context dict[str, Any] | None

Context information where error occurred

None
suggestions list[str] | None

List of suggested solutions

None
cause Exception | None

Original exception that caused this error

None
Source code in src/jinpy_utils/base/exceptions.py
class JPYBaseException(Exception):
    """Base exception class for all jinpy-utils exceptions.

    This exception provides structured error information using Pydantic models
    and supports JSON serialization for better error handling and logging.

    Args:
        error_code: Unique identifier for the error type
        message: Human-readable error description
        details: Additional structured error information
        context: Context information where error occurred
        suggestions: List of suggested solutions
        cause: Original exception that caused this error
    """

    def __init__(  # noqa: PLR0913
        self,
        error_code: str,
        message: str,
        details: dict[str, Any] | None = None,
        context: dict[str, Any] | None = None,
        suggestions: list[str] | None = None,
        cause: Exception | None = None,
    ) -> None:
        """Initialize the exception with structured error data."""
        self.error_details = ErrorDetails(
            error_code=error_code,
            message=message,
            details=details or {},
            context=context or {},
            suggestions=suggestions or [],
        )
        self.cause = cause
        super().__init__(self.error_details.message)

    def __str__(self) -> str:
        """Return string representation of the exception."""
        return f"[{self.error_details.error_code}] {self.error_details.message}"

    def __repr__(self) -> str:
        """Return detailed string representation."""
        return (
            f"{self.__class__.__name__}("
            f"error_code='{self.error_details.error_code}', "
            f"message='{self.error_details.message}'"
            f")"
        )

    def to_dict(self) -> dict[str, Any]:
        """Convert exception to dictionary format."""
        result: dict[str, Any] = self.error_details.model_dump()
        result.update(
            {
                "exception_type": self.__class__.__name__,
                "traceback": traceback.format_exc() if self.cause else None,
            }
        )
        return result

    def to_json(self) -> str:
        """Convert exception to JSON format."""
        return str(self.error_details.model_dump_json())

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "JPYBaseException":
        """Create exception instance from dictionary."""
        return cls(
            error_code=data["error_code"],
            message=data["message"],
            details=data.get("details"),
            context=data.get("context"),
            suggestions=data.get("suggestions"),
        )

Functions

__init__

__init__(
    error_code: str,
    message: str,
    details: dict[str, Any] | None = None,
    context: dict[str, Any] | None = None,
    suggestions: list[str] | None = None,
    cause: Exception | None = None,
) -> None

Initialize the exception with structured error data.

Source code in src/jinpy_utils/base/exceptions.py
def __init__(  # noqa: PLR0913
    self,
    error_code: str,
    message: str,
    details: dict[str, Any] | None = None,
    context: dict[str, Any] | None = None,
    suggestions: list[str] | None = None,
    cause: Exception | None = None,
) -> None:
    """Initialize the exception with structured error data."""
    self.error_details = ErrorDetails(
        error_code=error_code,
        message=message,
        details=details or {},
        context=context or {},
        suggestions=suggestions or [],
    )
    self.cause = cause
    super().__init__(self.error_details.message)

__str__

__str__() -> str

Return string representation of the exception.

Source code in src/jinpy_utils/base/exceptions.py
def __str__(self) -> str:
    """Return string representation of the exception."""
    return f"[{self.error_details.error_code}] {self.error_details.message}"

__repr__

__repr__() -> str

Return detailed string representation.

Source code in src/jinpy_utils/base/exceptions.py
def __repr__(self) -> str:
    """Return detailed string representation."""
    return (
        f"{self.__class__.__name__}("
        f"error_code='{self.error_details.error_code}', "
        f"message='{self.error_details.message}'"
        f")"
    )

to_dict

to_dict() -> dict[str, Any]

Convert exception to dictionary format.

Source code in src/jinpy_utils/base/exceptions.py
def to_dict(self) -> dict[str, Any]:
    """Convert exception to dictionary format."""
    result: dict[str, Any] = self.error_details.model_dump()
    result.update(
        {
            "exception_type": self.__class__.__name__,
            "traceback": traceback.format_exc() if self.cause else None,
        }
    )
    return result

to_json

to_json() -> str

Convert exception to JSON format.

Source code in src/jinpy_utils/base/exceptions.py
def to_json(self) -> str:
    """Convert exception to JSON format."""
    return str(self.error_details.model_dump_json())

from_dict classmethod

from_dict(data: dict[str, Any]) -> JPYBaseException

Create exception instance from dictionary.

Source code in src/jinpy_utils/base/exceptions.py
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "JPYBaseException":
    """Create exception instance from dictionary."""
    return cls(
        error_code=data["error_code"],
        message=data["message"],
        details=data.get("details"),
        context=data.get("context"),
        suggestions=data.get("suggestions"),
    )

Derived Exception Classes

JPYConfigurationError

JPYConfigurationError

Bases: JPYBaseException

Exception raised for configuration-related errors.

Source code in src/jinpy_utils/base/exceptions.py
class JPYConfigurationError(JPYBaseException):
    """Exception raised for configuration-related errors."""

    def __init__(
        self,
        message: str,
        config_key: str | None = None,
        config_value: Any | None = None,
        expected_type: type | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize configuration error."""
        details = kwargs.pop("details", {})
        if config_key:
            details["config_key"] = config_key
        if config_value is not None:
            details["config_value"] = str(config_value)
        if expected_type:
            details["expected_type"] = expected_type.__name__

        super().__init__(
            error_code="CONFIGURATION_ERROR",
            message=message,
            details=details,
            suggestions=kwargs.pop(
                "suggestions",
                [
                    "Check configuration file syntax",
                    "Verify environment variables",
                    "Ensure all required settings are provided",
                ],
            ),
            **kwargs,
        )

Functions

__init__

__init__(
    message: str,
    config_key: str | None = None,
    config_value: Any | None = None,
    expected_type: type | None = None,
    **kwargs: Any
) -> None

Initialize configuration error.

Source code in src/jinpy_utils/base/exceptions.py
def __init__(
    self,
    message: str,
    config_key: str | None = None,
    config_value: Any | None = None,
    expected_type: type | None = None,
    **kwargs: Any,
) -> None:
    """Initialize configuration error."""
    details = kwargs.pop("details", {})
    if config_key:
        details["config_key"] = config_key
    if config_value is not None:
        details["config_value"] = str(config_value)
    if expected_type:
        details["expected_type"] = expected_type.__name__

    super().__init__(
        error_code="CONFIGURATION_ERROR",
        message=message,
        details=details,
        suggestions=kwargs.pop(
            "suggestions",
            [
                "Check configuration file syntax",
                "Verify environment variables",
                "Ensure all required settings are provided",
            ],
        ),
        **kwargs,
    )

JPYCacheError

JPYCacheError

Bases: JPYBaseException

Exception raised for caching-related errors.

Source code in src/jinpy_utils/base/exceptions.py
class JPYCacheError(JPYBaseException):
    """Exception raised for caching-related errors."""

    def __init__(
        self,
        message: str,
        cache_key: str | None = None,
        cache_backend: str | None = None,
        operation: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize cache error."""
        details = kwargs.pop("details", {})
        if cache_key:
            details["cache_key"] = cache_key
        if cache_backend:
            details["cache_backend"] = cache_backend
        if operation:
            details["operation"] = operation

        super().__init__(
            error_code="CACHE_ERROR",
            message=message,
            details=details,
            suggestions=kwargs.pop(
                "suggestions",
                [
                    "Check cache backend connectivity",
                    "Verify cache key format",
                    "Ensure sufficient memory/storage",
                    "Check cache backend configuration",
                ],
            ),
            **kwargs,
        )

Functions

__init__

__init__(
    message: str,
    cache_key: str | None = None,
    cache_backend: str | None = None,
    operation: str | None = None,
    **kwargs: Any
) -> None

Initialize cache error.

Source code in src/jinpy_utils/base/exceptions.py
def __init__(
    self,
    message: str,
    cache_key: str | None = None,
    cache_backend: str | None = None,
    operation: str | None = None,
    **kwargs: Any,
) -> None:
    """Initialize cache error."""
    details = kwargs.pop("details", {})
    if cache_key:
        details["cache_key"] = cache_key
    if cache_backend:
        details["cache_backend"] = cache_backend
    if operation:
        details["operation"] = operation

    super().__init__(
        error_code="CACHE_ERROR",
        message=message,
        details=details,
        suggestions=kwargs.pop(
            "suggestions",
            [
                "Check cache backend connectivity",
                "Verify cache key format",
                "Ensure sufficient memory/storage",
                "Check cache backend configuration",
            ],
        ),
        **kwargs,
    )

JPYDatabaseError

JPYDatabaseError

Bases: JPYBaseException

Exception raised for database/ORM-related errors.

Source code in src/jinpy_utils/base/exceptions.py
class JPYDatabaseError(JPYBaseException):
    """Exception raised for database/ORM-related errors."""

    def __init__(
        self,
        message: str,
        table_name: str | None = None,
        operation: str | None = None,
        query: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize database error."""
        details = kwargs.pop("details", {})
        if table_name:
            details["table_name"] = table_name
        if operation:
            details["operation"] = operation
        if query:
            details["query"] = query

        super().__init__(
            error_code="DATABASE_ERROR",
            message=message,
            details=details,
            suggestions=kwargs.pop(
                "suggestions",
                [
                    "Check database connection",
                    "Verify table/column names",
                    "Ensure proper database permissions",
                    "Check query syntax",
                ],
            ),
            **kwargs,
        )

Functions

__init__

__init__(
    message: str,
    table_name: str | None = None,
    operation: str | None = None,
    query: str | None = None,
    **kwargs: Any
) -> None

Initialize database error.

Source code in src/jinpy_utils/base/exceptions.py
def __init__(
    self,
    message: str,
    table_name: str | None = None,
    operation: str | None = None,
    query: str | None = None,
    **kwargs: Any,
) -> None:
    """Initialize database error."""
    details = kwargs.pop("details", {})
    if table_name:
        details["table_name"] = table_name
    if operation:
        details["operation"] = operation
    if query:
        details["query"] = query

    super().__init__(
        error_code="DATABASE_ERROR",
        message=message,
        details=details,
        suggestions=kwargs.pop(
            "suggestions",
            [
                "Check database connection",
                "Verify table/column names",
                "Ensure proper database permissions",
                "Check query syntax",
            ],
        ),
        **kwargs,
    )

JPYLoggingError

JPYLoggingError

Bases: JPYBaseException

Exception raised for logging-related errors.

Source code in src/jinpy_utils/base/exceptions.py
class JPYLoggingError(JPYBaseException):
    """Exception raised for logging-related errors."""

    def __init__(
        self,
        message: str,
        logger_name: str | None = None,
        log_level: str | None = None,
        handler_type: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize logging error."""
        details = kwargs.pop("details", {})
        if logger_name:
            details["logger_name"] = logger_name
        if log_level:
            details["log_level"] = log_level
        if handler_type:
            details["handler_type"] = handler_type

        super().__init__(
            error_code="LOGGING_ERROR",
            message=message,
            details=details,
            suggestions=kwargs.pop(
                "suggestions",
                [
                    "Check log file permissions",
                    "Verify log directory exists",
                    "Ensure proper log level configuration",
                    "Check logging handler configuration",
                ],
            ),
            **kwargs,
        )

Functions

__init__

__init__(
    message: str,
    logger_name: str | None = None,
    log_level: str | None = None,
    handler_type: str | None = None,
    **kwargs: Any
) -> None

Initialize logging error.

Source code in src/jinpy_utils/base/exceptions.py
def __init__(
    self,
    message: str,
    logger_name: str | None = None,
    log_level: str | None = None,
    handler_type: str | None = None,
    **kwargs: Any,
) -> None:
    """Initialize logging error."""
    details = kwargs.pop("details", {})
    if logger_name:
        details["logger_name"] = logger_name
    if log_level:
        details["log_level"] = log_level
    if handler_type:
        details["handler_type"] = handler_type

    super().__init__(
        error_code="LOGGING_ERROR",
        message=message,
        details=details,
        suggestions=kwargs.pop(
            "suggestions",
            [
                "Check log file permissions",
                "Verify log directory exists",
                "Ensure proper log level configuration",
                "Check logging handler configuration",
            ],
        ),
        **kwargs,
    )

JPYValidationError

JPYValidationError

Bases: JPYBaseException

Exception raised for data validation errors.

Source code in src/jinpy_utils/base/exceptions.py
class JPYValidationError(JPYBaseException):
    """Exception raised for data validation errors."""

    def __init__(
        self,
        message: str,
        field_name: str | None = None,
        field_value: Any | None = None,
        validation_rule: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize validation error."""
        details = kwargs.pop("details", {})
        if field_name:
            details["field_name"] = field_name
        if field_value is not None:
            details["field_value"] = str(field_value)
        if validation_rule:
            details["validation_rule"] = validation_rule

        super().__init__(
            error_code="VALIDATION_ERROR",
            message=message,
            details=details,
            suggestions=kwargs.pop(
                "suggestions",
                [
                    "Check input data format",
                    "Verify required fields are provided",
                    "Ensure data types match expected format",
                    "Review validation rules",
                ],
            ),
            **kwargs,
        )

Functions

__init__

__init__(
    message: str,
    field_name: str | None = None,
    field_value: Any | None = None,
    validation_rule: str | None = None,
    **kwargs: Any
) -> None

Initialize validation error.

Source code in src/jinpy_utils/base/exceptions.py
def __init__(
    self,
    message: str,
    field_name: str | None = None,
    field_value: Any | None = None,
    validation_rule: str | None = None,
    **kwargs: Any,
) -> None:
    """Initialize validation error."""
    details = kwargs.pop("details", {})
    if field_name:
        details["field_name"] = field_name
    if field_value is not None:
        details["field_value"] = str(field_value)
    if validation_rule:
        details["validation_rule"] = validation_rule

    super().__init__(
        error_code="VALIDATION_ERROR",
        message=message,
        details=details,
        suggestions=kwargs.pop(
            "suggestions",
            [
                "Check input data format",
                "Verify required fields are provided",
                "Ensure data types match expected format",
                "Review validation rules",
            ],
        ),
        **kwargs,
    )

JPYConnectionError

JPYConnectionError

Bases: JPYBaseException

Exception raised for connection-related errors.

Source code in src/jinpy_utils/base/exceptions.py
class JPYConnectionError(JPYBaseException):
    """Exception raised for connection-related errors."""

    def __init__(
        self,
        message: str,
        service_name: str | None = None,
        host: str | None = None,
        port: int | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize connection error."""
        details = kwargs.pop("details", {})
        if service_name:
            details["service_name"] = service_name
        if host:
            details["host"] = host
        if port:
            details["port"] = port

        super().__init__(
            error_code="CONNECTION_ERROR",
            message=message,
            details=details,
            suggestions=kwargs.pop(
                "suggestions",
                [
                    "Check network connectivity",
                    "Verify service is running",
                    "Check firewall settings",
                    "Ensure correct host and port",
                ],
            ),
            **kwargs,
        )

Functions

__init__

__init__(
    message: str,
    service_name: str | None = None,
    host: str | None = None,
    port: int | None = None,
    **kwargs: Any
) -> None

Initialize connection error.

Source code in src/jinpy_utils/base/exceptions.py
def __init__(
    self,
    message: str,
    service_name: str | None = None,
    host: str | None = None,
    port: int | None = None,
    **kwargs: Any,
) -> None:
    """Initialize connection error."""
    details = kwargs.pop("details", {})
    if service_name:
        details["service_name"] = service_name
    if host:
        details["host"] = host
    if port:
        details["port"] = port

    super().__init__(
        error_code="CONNECTION_ERROR",
        message=message,
        details=details,
        suggestions=kwargs.pop(
            "suggestions",
            [
                "Check network connectivity",
                "Verify service is running",
                "Check firewall settings",
                "Ensure correct host and port",
            ],
        ),
        **kwargs,
    )

Usage Examples

Basic Exception Usage

from jinpy_utils.base import JPYConfigurationError

try:
    # Some operation that might fail
    if not api_key:
        raise JPYConfigurationError(
            message="API key is required",
            config_key="API_KEY",
            suggestions=[
                "Set the API_KEY environment variable",
                "Add api_key to your configuration file"
            ]
        )
except JPYConfigurationError as e:
    print(f"Configuration error: {e.message}")
    print(f"Config key: {e.config_key}")
    print(f"Suggestions: {', '.join(e.suggestions)}")

JSON Serialization

from jinpy_utils.base import JPYDatabaseError

try:
    # Database operation
    raise JPYDatabaseError(
        message="Connection timeout",
        database_name="user_db",
        table="users",
        operation="SELECT",
        timeout_seconds=30
    )
except JPYDatabaseError as e:
    # Convert to JSON for API response
    error_json = e.to_json()
    print(error_json)

    # Convert to dictionary for logging
    error_dict = e.to_dict()
    logger.error("Database operation failed", **error_dict)

Validation Errors

from jinpy_utils.base import JPYValidationError

def validate_user_input(data: dict):
    """Validate user input with detailed error reporting."""
    if not data.get("email"):
        raise JPYValidationError(
            message="Email is required",
            field="email",
            value=data.get("email"),
            validation_rule="required",
            suggestions=["Provide a valid email address"]
        )

    if "@" not in data.get("email", ""):
        raise JPYValidationError(
            message="Invalid email format",
            field="email",
            value=data["email"],
            validation_rule="email_format",
            expected_format="user@domain.com",
            suggestions=["Ensure email contains @ symbol and domain"]
        )

# Usage
try:
    validate_user_input({"email": "invalid-email"})
except JPYValidationError as e:
    print(f"Validation failed for field '{e.field}': {e.message}")
    print(f"Current value: '{e.value}'")
    print(f"Expected format: {e.expected_format}")

Connection Errors with Retry Logic

from jinpy_utils.base import JPYConnectionError
import time

def connect_with_retry(endpoint: str, max_retries: int = 3):
    """Connect to endpoint with retry logic and detailed error reporting."""
    for attempt in range(max_retries):
        try:
            # Simulated connection attempt
            if attempt < 2:  # Simulate failures
                raise JPYConnectionError(
                    message=f"Connection failed (attempt {attempt + 1})",
                    endpoint=endpoint,
                    protocol="HTTPS",
                    port=443,
                    timeout_seconds=30,
                    retry_count=attempt + 1,
                    max_retries=max_retries,
                    suggestions=[
                        "Check network connectivity",
                        "Verify endpoint URL",
                        "Check firewall settings"
                    ]
                )

            # Success case
            return "Connected successfully"

        except JPYConnectionError as e:
            if attempt == max_retries - 1:
                # Final failure
                e.add_context("final_attempt", True)
                e.add_suggestion("Contact system administrator")
                raise
            else:
                # Retry
                wait_time = 2 ** attempt
                time.sleep(wait_time)
                continue

# Usage
try:
    result = connect_with_retry("https://api.example.com")
except JPYConnectionError as e:
    print(f"Connection failed after {e.retry_count} attempts")
    print(f"Error: {e.message}")
    print(f"Suggestions: {', '.join(e.suggestions)}")

Cache Errors

from jinpy_utils.base import JPYCacheError

class CacheManager:
    """Example cache manager with structured error handling."""

    def get(self, key: str):
        """Get value from cache with error handling."""
        try:
            # Simulated cache operation
            if key.startswith("invalid_"):
                raise JPYCacheError(
                    message="Invalid cache key format",
                    cache_key=key,
                    operation="GET",
                    cache_type="redis",
                    expected_pattern="^[a-zA-Z0-9_-]+$",
                    suggestions=[
                        "Use only alphanumeric characters, underscores, and hyphens",
                        "Remove 'invalid_' prefix from key"
                    ]
                )

            # Return mock value
            return f"value_for_{key}"

        except JPYCacheError as e:
            # Log the error with structured data
            logger.error("Cache operation failed", **e.to_dict())
            raise

# Usage
cache = CacheManager()
try:
    value = cache.get("invalid_key_name!")
except JPYCacheError as e:
    print(f"Cache error: {e.message}")
    print(f"Operation: {e.operation} on key '{e.cache_key}'")
    print(f"Expected pattern: {e.expected_pattern}")

Advanced Usage

Exception Chaining

from jinpy_utils.base import JPYDatabaseError, JPYConfigurationError

def connect_to_database():
    """Connect to database with proper exception chaining."""
    try:
        # Check configuration first
        if not database_url:
            raise JPYConfigurationError(
                message="Database URL not configured",
                config_key="DATABASE_URL"
            )

        # Attempt connection
        # ... connection code ...

    except ConnectionError as e:
        # Chain the original exception
        raise JPYDatabaseError(
            message="Failed to connect to database",
            database_url=database_url,
            original_error=str(e),
            suggestions=[
                "Check database server status",
                "Verify connection parameters",
                "Check network connectivity"
            ]
        ) from e
    except JPYConfigurationError:
        # Re-raise configuration errors as-is
        raise
    except Exception as e:
        # Wrap unexpected errors
        raise JPYDatabaseError(
            message="Unexpected database error",
            original_error=str(e),
            error_type=type(e).__name__
        ) from e

Context Management

from contextlib import contextmanager
from jinpy_utils.base import JPYBaseException

@contextmanager
def error_context(operation: str, **context_data):
    """Add context to any jinpy exceptions that occur."""
    try:
        yield
    except JPYBaseException as e:
        # Add operation context to the exception
        e.add_context("operation", operation)
        for key, value in context_data.items():
            e.add_context(key, value)
        raise
    except Exception as e:
        # Wrap non-jinpy exceptions
        raise JPYBaseException(
            error_code="UNEXPECTED_ERROR",
            message=f"Unexpected error during {operation}",
            operation=operation,
            original_error=str(e),
            **context_data
        ) from e

# Usage
with error_context("user_registration", user_id=123, ip="192.168.1.1"):
    # Operations that might fail
    validate_user_data(user_data)
    save_user_to_database(user_data)

Custom Exception Types

from jinpy_utils.base import JPYBaseException

class JPYPaymentError(JPYBaseException):
    """Custom exception for payment-related errors."""

    def __init__(self, message: str, **kwargs):
        super().__init__(
            error_code="PAYMENT_ERROR",
            message=message,
            **kwargs
        )

class JPYAuthenticationError(JPYBaseException):
    """Custom exception for authentication errors."""

    def __init__(self, message: str, **kwargs):
        super().__init__(
            error_code="AUTHENTICATION_ERROR",
            message=message,
            **kwargs
        )

# Usage
def process_payment(amount: float, payment_method: str):
    """Process payment with custom error handling."""
    if amount <= 0:
        raise JPYPaymentError(
            message="Invalid payment amount",
            amount=amount,
            currency="USD",
            suggestions=["Amount must be greater than 0"]
        )

    if payment_method not in ["credit_card", "paypal", "bank_transfer"]:
        raise JPYPaymentError(
            message="Unsupported payment method",
            payment_method=payment_method,
            supported_methods=["credit_card", "paypal", "bank_transfer"],
            suggestions=["Use one of the supported payment methods"]
        )

Error Recovery Patterns

from jinpy_utils.base import JPYBaseException, JPYConnectionError, JPYCacheError

def robust_data_fetch(key: str):
    """Fetch data with multiple fallback strategies."""
    errors = []

    # Try cache first
    try:
        return cache.get(key)
    except JPYCacheError as e:
        errors.append(e)
        logger.warning("Cache fetch failed, trying database", error=e.to_dict())

    # Try database
    try:
        data = database.get(key)
        # Repopulate cache for next time
        try:
            cache.set(key, data)
        except JPYCacheError:
            pass  # Cache update failure is not critical
        return data
    except JPYConnectionError as e:
        errors.append(e)
        logger.warning("Database fetch failed, trying API", error=e.to_dict())

    # Try external API
    try:
        return api_client.fetch(key)
    except JPYConnectionError as e:
        errors.append(e)

        # All strategies failed
        raise JPYBaseException(
            error_code="DATA_FETCH_FAILED",
            message=f"Failed to fetch data for key '{key}' using all strategies",
            key=key,
            strategies_tried=["cache", "database", "api"],
            errors=[error.to_dict() for error in errors],
            suggestions=[
                "Check system connectivity",
                "Verify data exists",
                "Try again later"
            ]
        )

Logging Integration

from jinpy_utils.logger import get_logger
from jinpy_utils.base import JPYBaseException

logger = get_logger("exception_handler")

def handle_service_error(func):
    """Decorator for consistent service error handling."""
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except JPYBaseException as e:
            # Log structured error data
            logger.error(
                f"Service error in {func.__name__}",
                function=func.__name__,
                **e.to_dict()
            )
            raise
        except Exception as e:
            # Log unexpected errors
            logger.error(
                f"Unexpected error in {func.__name__}",
                function=func.__name__,
                error_type=type(e).__name__,
                error_message=str(e)
            )
            # Convert to structured exception
            raise JPYBaseException(
                error_code="UNEXPECTED_SERVICE_ERROR",
                message=f"Unexpected error in {func.__name__}",
                function=func.__name__,
                original_error=str(e)
            ) from e
    return wrapper

# Usage
@handle_service_error
def user_service_operation(user_id: int):
    """Example service operation with error handling."""
    # Service logic that might raise exceptions
    pass

Error Response Patterns

API Response Format

from flask import jsonify
from jinpy_utils.base import JPYBaseException

def create_error_response(error: JPYBaseException, status_code: int = 400):
    """Create standardized API error response."""
    response_data = {
        "error": {
            "code": error.error_code,
            "message": error.message,
            "timestamp": error.timestamp,
            "suggestions": error.suggestions
        }
    }

    # Add specific error details if available
    error_dict = error.to_dict()
    details = {k: v for k, v in error_dict.items()
               if k not in ["error_code", "message", "timestamp", "suggestions"]}

    if details:
        response_data["error"]["details"] = details

    return jsonify(response_data), status_code

# Usage in Flask route
@app.route("/api/users", methods=["POST"])
def create_user():
    try:
        # User creation logic
        return jsonify({"user": user_data}), 201
    except JPYBaseException as e:
        return create_error_response(e, 400)
    except Exception as e:
        # Convert unexpected errors
        error = JPYBaseException(
            error_code="INTERNAL_SERVER_ERROR",
            message="An unexpected error occurred",
            original_error=str(e)
        )
        return create_error_response(error, 500)