CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-dynaconf

The dynamic configurator for your Python Project

Pending
Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Exception classes and error handling patterns for configuration parsing, validation failures, and format errors with detailed error reporting and debugging information. Understanding dynaconf's error handling helps debug configuration issues and implement robust error recovery.

Capabilities

Validation Errors

Handle configuration validation failures with detailed error information.

class ValidationError(Exception):
    """Exception raised when configuration validation fails."""
    def __init__(self, message: str, details=None): ...
    
    @property
    def message(self) -> str:
        """Primary error message."""
        ...
    
    @property
    def details(self) -> list:
        """List of detailed error information including failed validators."""
        ...

Usage examples:

from dynaconf import Dynaconf, Validator, ValidationError

settings = Dynaconf(
    validators=[
        Validator("DATABASE_URL", must_exist=True),
        Validator("PORT", cast=int, gte=1000, lte=65535),
        Validator("DEBUG", cast=bool),
    ]
)

try:
    settings.validators.validate()
except ValidationError as e:
    print(f"Validation failed: {e.message}")
    
    # Access detailed error information
    for detail in e.details:
        print(f"  Failed validator: {detail['validator']}")
        print(f"  Key: {detail['key']}")
        print(f"  Issue: {detail['issue']}")
        
    # Log for debugging
    import logging
    logging.error(f"Configuration validation error: {e}")

Format Errors

Handle errors in configuration value formatting and lazy evaluation.

class DynaconfFormatError(Exception):
    """Exception raised when formatting lazy variables fails."""
    pass

Usage examples:

from dynaconf import Dynaconf, DynaconfFormatError

# Configuration with invalid formatting
# settings.toml:
# message = "Hello @{UNDEFINED_VAR}!"

settings = Dynaconf(settings_files=["settings.toml"])

try:
    formatted_message = settings.message
except DynaconfFormatError as e:
    print(f"Format error in configuration: {e}")
    
    # Provide fallback value
    formatted_message = "Hello World!"
    
    # Or use safe access
    formatted_message = settings.get("message", "Default message")

Parse Errors

Handle errors in parsing @cast directives and type conversions.

class DynaconfParseError(Exception):
    """Exception raised when parsing @cast directives fails."""
    pass

Usage examples:

from dynaconf import Dynaconf, DynaconfParseError

# Configuration with invalid casting
# settings.toml:
# port = "@int invalid_number"
# config = "@json {invalid json}"

settings = Dynaconf(settings_files=["settings.toml"])

try:
    port = settings.port
except DynaconfParseError as e:
    print(f"Parse error: {e}")
    # Use default value
    port = 8000

try:
    config_data = settings.config
except DynaconfParseError as e:
    print(f"JSON parse error: {e}")
    # Use empty dict as fallback
    config_data = {}

Error Handling Patterns

Comprehensive Error Handling

Handle all dynaconf exceptions in a unified way.

from dynaconf import (
    Dynaconf, ValidationError, DynaconfFormatError, 
    DynaconfParseError, Validator
)
import logging

def create_robust_settings():
    """Create settings with comprehensive error handling."""
    try:
        settings = Dynaconf(
            envvar_prefix="MYAPP",
            settings_files=["config.toml", "local.yaml"],
            environments=True,
            validators=[
                Validator("DATABASE_URL", must_exist=True),
                Validator("PORT", cast=int, default=8000),
                Validator("DEBUG", cast=bool, default=False),
            ]
        )
        
        # Validate configuration
        settings.validators.validate()
        
        return settings
        
    except ValidationError as e:
        logging.error(f"Configuration validation failed: {e.message}")
        for detail in e.details:
            logging.error(f"  {detail}")
        raise
        
    except DynaconfFormatError as e:
        logging.error(f"Configuration format error: {e}")
        raise
        
    except DynaconfParseError as e:
        logging.error(f"Configuration parse error: {e}")
        raise
        
    except Exception as e:
        logging.error(f"Unexpected configuration error: {e}")
        raise

# Usage with error handling
try:
    settings = create_robust_settings()
    print("Configuration loaded successfully!")
except Exception as e:
    print(f"Failed to load configuration: {e}")
    # Use fallback configuration or exit gracefully

Graceful Degradation

Implement fallback strategies when configuration loading fails.

def get_settings_with_fallback():
    """Get settings with graceful degradation to defaults."""
    default_settings = {
        'DATABASE_URL': 'sqlite:///default.db',
        'PORT': 8000,
        'DEBUG': True,
        'SECRET_KEY': 'development-only-key',
    }
    
    try:
        # Try to load full configuration
        settings = Dynaconf(
            settings_files=["config.toml", "production.yaml"],
            environments=True,
            validators=[
                Validator("DATABASE_URL", must_exist=True),
                Validator("SECRET_KEY", must_exist=True),
            ]
        )
        
        settings.validators.validate()
        return settings
        
    except ValidationError as e:
        print(f"Validation failed, using partial configuration: {e.message}")
        
        # Create minimal settings with available values
        settings = Dynaconf(environments=False)
        
        # Apply defaults for missing required values
        for key, default_value in default_settings.items():
            if not settings.exists(key):
                settings.set(key, default_value)
        
        return settings
        
    except (DynaconfFormatError, DynaconfParseError) as e:
        print(f"Configuration parse error, using defaults: {e}")
        
        # Return minimal settings with defaults
        settings = Dynaconf(environments=False)
        for key, value in default_settings.items():
            settings.set(key, value)
        
        return settings

# Safe configuration access
def safe_get_setting(settings, key, default=None, cast=None):
    """Safely get setting value with error handling."""
    try:
        return settings.get(key, default=default, cast=cast)
    except (DynaconfFormatError, DynaconfParseError) as e:
        logging.warning(f"Error accessing setting '{key}': {e}")
        return default

Environment-Specific Error Handling

Handle errors differently based on the environment.

def handle_configuration_error(error, current_env="development"):
    """Handle configuration errors based on environment."""
    
    if current_env == "production":
        # In production, log error and exit
        logging.critical(f"Production configuration error: {error}")
        import sys
        sys.exit(1)
        
    elif current_env == "development":
        # In development, warn and continue with defaults
        logging.warning(f"Development configuration error: {error}")
        print(f"Warning: {error}")
        return True  # Continue execution
        
    elif current_env == "testing":
        # In testing, use minimal configuration
        logging.info(f"Testing configuration error (expected): {error}")
        return True
        
    else:
        # Unknown environment, be conservative
        logging.error(f"Configuration error in unknown environment: {error}")
        raise error

def create_environment_aware_settings():
    """Create settings with environment-aware error handling."""
    current_env = os.environ.get("APP_ENV", "development")
    
    try:
        settings = Dynaconf(
            envvar_prefix="MYAPP",
            settings_files=["config.toml"],
            environments=True,
            validators=[
                Validator("DATABASE_URL", must_exist=True),
                # More strict validation in production
                Validator("SECRET_KEY", must_exist=(current_env == "production")),
            ]
        )
        
        settings.validators.validate()
        return settings
        
    except ValidationError as e:
        if handle_configuration_error(e, current_env):
            # Create minimal settings for non-production
            return Dynaconf(environments=False)
        else:
            raise

Custom Error Classes

Create application-specific error classes for better error handling.

class ConfigurationError(Exception):
    """Base class for configuration-related errors."""
    pass

class MissingRequiredSettingError(ConfigurationError):
    """Error for missing required configuration values."""
    def __init__(self, key, environment=None):
        self.key = key
        self.environment = environment
        env_msg = f" in environment '{environment}'" if environment else ""
        super().__init__(f"Required setting '{key}' is missing{env_msg}")

class InvalidSettingValueError(ConfigurationError):
    """Error for invalid configuration values."""
    def __init__(self, key, value, expected_type):
        self.key = key
        self.value = value
        self.expected_type = expected_type
        super().__init__(
            f"Invalid value for '{key}': {value} (expected {expected_type})"
        )

def validate_settings_strictly(settings):
    """Perform strict validation with custom error types."""
    required_settings = {
        "DATABASE_URL": str,
        "SECRET_KEY": str,
        "PORT": int,
        "DEBUG": bool,
    }
    
    for key, expected_type in required_settings.items():
        # Check existence
        if not settings.exists(key):
            raise MissingRequiredSettingError(key, settings.current_env)
        
        # Check type
        value = settings.get(key)
        if not isinstance(value, expected_type):
            raise InvalidSettingValueError(key, value, expected_type)

# Usage with custom error handling
try:
    settings = Dynaconf(settings_files=["config.toml"])
    validate_settings_strictly(settings)
except MissingRequiredSettingError as e:
    print(f"Missing required setting: {e.key}")
    # Handle missing setting
except InvalidSettingValueError as e:
    print(f"Invalid setting value: {e.key} = {e.value}")
    # Handle invalid value
except ConfigurationError as e:
    print(f"Configuration error: {e}")
    # Handle general configuration error

Debugging Configuration Issues

Tools and patterns for debugging configuration problems.

def debug_configuration_loading(settings):
    """Debug configuration loading issues."""
    from dynaconf import inspect_settings, get_history
    
    print("=== CONFIGURATION DEBUG REPORT ===")
    print(f"Current environment: {settings.current_env}")
    print(f"Loaded environments: {settings.loaded_envs}")
    print()
    
    # Show loading history
    history = get_history(settings, include_internal=False)
    print("Loading history:")
    for entry in history[-10:]:  # Last 10 entries
        print(f"  {entry['key']} = {entry['value']} (from {entry['loader']})")
    print()
    
    # Show validation issues
    try:
        settings.validators.validate()
        print("All validations passed!")
    except ValidationError as e:
        print("Validation failures:")
        for detail in e.details:
            print(f"  {detail}")
    print()
    
    # Show inspection report
    inspect_settings(settings, dumper="yaml", print_report=True)

def trace_setting_value(settings, key):
    """Trace how a specific setting got its value."""
    from dynaconf import get_history
    
    print(f"=== TRACING SETTING: {key} ===")
    
    # Current value
    if settings.exists(key):
        current_value = settings.get(key)
        print(f"Current value: {current_value}")
    else:
        print("Setting does not exist")
        return
    
    # Loading history for this key
    history = get_history(settings, key=key)
    if history:
        print("\nLoading history:")
        for i, entry in enumerate(history):
            print(f"  {i+1}. {entry['value']} (from {entry['loader']})")
    else:
        print("No loading history found")
    
    # Environment context
    print(f"\nEnvironment: {settings.current_env}")
    print(f"Available environments: {settings.loaded_envs}")

# Usage for debugging
def debug_settings_issue():
    """Debug a specific settings issue."""
    try:
        settings = Dynaconf(
            settings_files=["config.toml", "local.yaml"],
            environments=True
        )
        
        # Debug specific setting
        trace_setting_value(settings, "DATABASE_URL")
        
        # Full debug report
        debug_configuration_loading(settings)
        
    except Exception as e:
        print(f"Error during debugging: {e}")
        import traceback
        traceback.print_exc()

Install with Tessl CLI

npx tessl i tessl/pypi-dynaconf

docs

cli.md

core-configuration.md

error-handling.md

framework-integration.md

index.md

utilities.md

validation.md

tile.json