Utilities for easy adoption and incremental migration to structured error handling
Adoption Utilities
These utilities make it easy to incrementally adopt structured error handling in existing codebases without requiring large-scale refactoring.
error_boundary
A context manager that automatically enriches exceptions with context and converts them to structured errors.
error_boundary
def error_boundary( error_type:Type=BaseError, # Error type to raise message:Optional=None, # User-facing message (uses original if None) context:Optional=None, # Error context operation:Optional=None, # Operation name (added to context) job_id:Optional=None, # Job ID (added to context) plugin_id:Optional=None, # Plugin ID (added to context) worker_pid:Optional=None, # Worker PID (added to context) severity:ErrorSeverity=<ErrorSeverity.ERROR: 'error'>, # Error severity is_retryable:Optional=None, # Override retryable flag catch:tuple=(<class'Exception'>,), # Exception types to catch extra_context:VAR_KEYWORD):
Context manager that catches exceptions and wraps them in structured errors.
Caught: ConfigurationError
Message: Failed to load configuration
Debug: Failed to load configuration | Debug: Original error: FileNotFoundError: /config/settings.json | Caused by: FileNotFoundError: /config/settings.json
Context: load_config
# With context fieldstry:with error_boundary( error_type=PluginError, operation="initialize_plugin", plugin_id="whisper_large", model_path="/models/whisper-large-v3.pt"# Extra context ):raiseImportError("No module named 'torch'")except PluginError as e:print(f"\n{type(e).__name__}: {e.get_user_message()}")print(f"Plugin: {e.context.plugin_id}")print(f"Extra: {e.context.extra}")
PluginError: No module named 'torch'
Plugin: whisper_large
Extra: {'model_path': '/models/whisper-large-v3.pt'}
with_error_handling
A decorator that automatically wraps function errors with context.
with_error_handling
def with_error_handling( error_type:Type=BaseError, # Error type to raise message:Optional=None, # User-facing message operation:Optional=None, # Operation name (uses function name if None) severity:ErrorSeverity=<ErrorSeverity.ERROR: 'error'>, # Error severity is_retryable:Optional=None, # Override retryable flag catch:tuple=(<class'Exception'>,), # Exception types to catch context_from_args:Optional=None, # Map arg names to context fields, e.g. {'job_id': 'job_id'}):
Decorator that wraps function errors with structured error handling.
Example: with_error_handling decorator
# Simple decorator usage@with_error_handling( error_type=ValidationError, message="Configuration validation failed")def validate_config(config):"""Validate a configuration dictionary."""if'model_id'notin config:raiseValueError("model_id is required")returnTrue# Test ittry: validate_config({}) # Missing model_idexcept ValidationError as e:print(f"Caught: {type(e).__name__}")print(f"Message: {e.get_user_message()}")print(f"Operation: {e.context.operation}")
# With context from arguments@with_error_handling( error_type=WorkerError, context_from_args={'job_id': 'job_id', 'worker_pid': 'worker_pid'})def execute_job(job_id, worker_pid, data):"""Execute a job in a worker."""if data isNone:raiseRuntimeError("No data provided")returnf"Processed {data}"# Test ittry: execute_job(job_id="job-123", worker_pid=54321, data=None)except WorkerError as e:print(f"\n{type(e).__name__}: {e.get_user_message()}")print(f"Job ID: {e.context.job_id}")print(f"Worker PID: {e.context.worker_pid}")print(f"Operation: {e.context.operation}")
WorkerError: No data provided
Job ID: job-123
Worker PID: 54321
Operation: execute_job
wrap_exception
Helper function to wrap an existing exception in a structured error type.
wrap_exception
def wrap_exception( exception:Exception, # Original exception to wrap error_type:Type=BaseError, # Error type to create message:Optional=None, # User-facing message (uses exception if None) context:Optional=None, # Error context severity:ErrorSeverity=<ErrorSeverity.ERROR: 'error'>, # Error severity is_retryable:Optional=None, # Override retryable flag context_kwargs:VAR_KEYWORD)->BaseError: # Wrapped structured error
Wrap an existing exception in a structured error type.
Example: wrap_exception
# Wrap a caught exceptiontry:import json json.loads('{bad json}')except json.JSONDecodeError as e:# Wrap in our error type error = wrap_exception( e, error_type=ConfigurationError, message="Failed to parse configuration file", operation="load_config", plugin_id="whisper_base" )print(f"Wrapped error: {type(error).__name__}")print(f"Message: {error.get_user_message()}")print(f"Debug: {error.get_debug_message()}")print(f"Context: {error.context.to_dict()}")
Wrapped error: ConfigurationError
Message: Failed to parse configuration file
Debug: Failed to parse configuration file | Debug: Original: JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) | Caused by: JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
Context: {'plugin_id': 'whisper_base', 'operation': 'load_config', 'timestamp': '2026-04-14T05:31:46.024619', 'extra': {}}
chain_error
Helper for explicit error chaining with enriched context.
chain_error
def chain_error( base_error:BaseError, # Original structured error new_message:str, # New user-facing message error_type:Optional=None, # New error type (uses base type if None) additional_context:Optional=None, # Additional context to add operation:Optional=None, # Update operation name severity:Optional=None, # Override severity)->BaseError: # New error with chained context
Chain a structured error with additional context.
Example: chain_error
# Simulate layered error handlingdef load_config_file():"""Low-level config loading."""raise ConfigurationError( message="Config file not found", debug_info="/config/whisper.json does not exist", context=ErrorContext(operation="read_file") )def initialize_plugin():"""Mid-level plugin initialization."""try: load_config_file()except ConfigurationError as e:# Chain with plugin contextraise chain_error( e, new_message="Plugin initialization failed", error_type=PluginError, operation="initialize_plugin", additional_context={"plugin_name": "Whisper Large"} )# Test chained errorstry: initialize_plugin()except PluginError as e:print(f"Error type: {type(e).__name__}")print(f"Message: {e.get_user_message()}")print(f"Operation: {e.context.operation}")print(f"Extra context: {e.context.extra}")print(f"\nDebug (includes chain):")print(f" {e.get_debug_message()}")
Error type: PluginError
Message: Plugin initialization failed
Operation: initialize_plugin
Extra context: {'plugin_name': 'Whisper Large'}
Debug (includes chain):
Plugin initialization failed | Debug: Config file not found | Debug: /config/whisper.json does not exist | Caused by: ConfigurationError: Config file not found
Usage Summary
These utilities enable different adoption strategies:
1. Context Manager (Quickest)
Wrap existing code blocks:
with error_boundary(error_type=PluginError, plugin_id="whisper"): existing_code()