# Error Handling and Debugging

Comprehensive exception hierarchy and debugging tools for template development including detailed error messages, source location tracking, and runtime debugging capabilities.

## Capabilities

### Exception Hierarchy

Jinja2 provides a comprehensive exception hierarchy for different types of template errors.

```python { .api }
class TemplateError(Exception):
    """
    Base class for all template-related errors.
    
    Attributes:
        message: Error message
        lineno: Line number where error occurred (if available)
        name: Template name where error occurred (if available)
        filename: Template filename where error occurred (if available)
    """

class TemplateNotFound(TemplateError):
    """
    Raised when a template cannot be found.
    
    Attributes:
        name: Template name that was not found
        message: Error message
    """

class TemplatesNotFound(TemplateNotFound):
    """
    Raised when multiple templates cannot be found (used by select_template).
    
    Attributes:
        names: List of template names that were not found
        message: Error message including all attempted names
    """

class TemplateSyntaxError(TemplateError):
    """
    Raised when template syntax is malformed.
    
    Attributes:
        message: Detailed syntax error message
        lineno: Line number of syntax error
        name: Template name
        filename: Template filename
        source: Template source code (if available)
    """

class TemplateRuntimeError(TemplateError):
    """
    Raised during template execution for runtime errors.
    
    Attributes:
        message: Runtime error message
        lineno: Line number where error occurred
        name: Template name
        filename: Template filename
    """

class TemplateAssertionError(TemplateRuntimeError):
    """
    Raised when template assertion fails ({% assert %} tag).
    
    Attributes:
        message: Assertion error message
        lineno: Line number of failed assertion
        name: Template name
        filename: Template filename
    """

class UndefinedError(TemplateRuntimeError):
    """
    Raised when undefined variable is used inappropriately.
    
    Attributes:
        message: Undefined error message
        name: Variable name that was undefined
        exc_info: Original exception info (if available)
    """

class SecurityError(TemplateRuntimeError):
    """
    Raised when template tries to perform insecure operations in sandbox mode.
    
    Attributes:
        message: Security violation description
        lineno: Line number where violation occurred
        name: Template name
        filename: Template filename
    """

class FilterArgumentError(TemplateRuntimeError):
    """
    Raised when filter is called with inappropriate arguments.
    
    Attributes:
        message: Argument error description
        filter_name: Name of filter that failed (if available)
        lineno: Line number where error occurred
        name: Template name
        filename: Template filename
    """
```

### Undefined Types for Debugging

Specialized undefined types that provide different behaviors for debugging and error handling.

```python { .api }
class Undefined:
    """
    Default undefined type that raises UndefinedError on most operations.
    
    Methods:
        _undefined_hint: Get hint about undefined variable
        _undefined_obj: Get undefined object info
        _undefined_name: Get undefined variable name
        _undefined_exception: Get undefined exception type
    """

class StrictUndefined(Undefined):
    """
    Undefined that raises errors on any operation including __str__ and __iter__.
    More strict than default Undefined for catching undefined usage early.
    """

class DebugUndefined(Undefined):
    """
    Undefined that shows debug information when printed instead of raising errors.
    Useful for template development and debugging.
    
    When rendered shows: {{ undefined value 'variable_name' }}
    """

class ChainableUndefined(Undefined):
    """
    Undefined that doesn't raise errors on getattr/getitem operations.
    Allows chaining operations on undefined values without immediate errors.
    
    Example: {{ undefined_obj.attr.nested_attr }} won't raise until final rendering
    """

def make_logging_undefined(logger=None, base=None):
    """
    Create an undefined class that logs undefined variable access.
    
    Parameters:
        logger: Logger instance to use (default: creates new logger)
        base: Base undefined class (default: Undefined)
        
    Returns:
        class: Custom undefined class that logs access attempts
    """
```

Usage examples:

```python
from jinja2 import Environment, DebugUndefined, StrictUndefined

# Debug mode - shows undefined variables without errors
debug_env = Environment(undefined=DebugUndefined)
template = debug_env.from_string('Hello {{ name }}! Age: {{ user.age }}')
result = template.render()
# Result: Hello {{ undefined value 'name' }}! Age: {{ undefined value 'user' }}

# Strict mode - raises errors immediately
strict_env = Environment(undefined=StrictUndefined)
template = strict_env.from_string('Hello {{ name }}!')
try:
    result = template.render()  # Raises UndefinedError
except UndefinedError as e:
    print(f'Undefined variable: {e}')

# Logging undefined access
import logging
logging.basicConfig(level=logging.WARNING)
LoggingUndefined = make_logging_undefined()
log_env = Environment(undefined=LoggingUndefined)
template = log_env.from_string('Hello {{ name }}!')
result = template.render()  # Logs warning about undefined 'name'
```

### Error Context and Debugging

Tools for understanding and debugging template errors with detailed context information.

```python { .api }
def get_template_module(env, template_name):
    """
    Get template module for introspection and debugging.
    
    Parameters:
        env: Jinja2 environment
        template_name: Name of template to introspect
        
    Returns:
        TemplateModule: Module with template exports and metadata
    """

def render_template_with_context(template, **context):
    """
    Render template and return both result and execution context.
    
    Parameters:
        template: Template instance
        **context: Template context variables
        
    Returns:
        tuple: (rendered_output, execution_context)
    """
```

## Error Handling Patterns

### Graceful Error Handling

Handle template errors gracefully in production applications:

```python
from jinja2 import Environment, TemplateNotFound, TemplateSyntaxError, TemplateRuntimeError
from jinja2 import FileSystemLoader, select_autoescape
import logging

logger = logging.getLogger(__name__)

class RobustTemplateRenderer:
    def __init__(self):
        self.env = Environment(
            loader=FileSystemLoader('templates'),
            autoescape=select_autoescape(),
            undefined=ChainableUndefined  # Don't fail on undefined variables
        )
    
    def render_template(self, template_name, fallback_template=None, **context):
        """
        Render template with comprehensive error handling.
        
        Parameters:
            template_name: Primary template to render
            fallback_template: Fallback template if primary fails
            **context: Template context variables
            
        Returns:
            str: Rendered template or error message
        """
        try:
            template = self.env.get_template(template_name)
            return template.render(**context)
            
        except TemplateNotFound as e:
            logger.error(f'Template not found: {e}')
            if fallback_template:
                try:
                    template = self.env.get_template(fallback_template)
                    return template.render(**context)
                except Exception as fallback_error:
                    logger.error(f'Fallback template failed: {fallback_error}')
            return f'<p>Template {template_name} not found</p>'
            
        except TemplateSyntaxError as e:
            logger.error(f'Template syntax error in {e.name} at line {e.lineno}: {e.message}')
            return f'<p>Template syntax error in {template_name}</p>'
            
        except TemplateRuntimeError as e:
            logger.error(f'Template runtime error in {e.name} at line {e.lineno}: {e.message}')
            return f'<p>Template runtime error in {template_name}</p>'
            
        except Exception as e:
            logger.error(f'Unexpected error rendering {template_name}: {e}')
            return f'<p>Error rendering template {template_name}</p>'

# Usage
renderer = RobustTemplateRenderer()
result = renderer.render_template('user_profile.html', 'default_profile.html', user=user_data)
```

### Development Error Display

Detailed error display for development environments:

```python
from jinja2 import Environment, TemplateSyntaxError, TemplateRuntimeError
from jinja2 import DictLoader, DebugUndefined
import traceback
import html

class DevelopmentTemplateRenderer:
    def __init__(self):
        self.env = Environment(
            loader=DictLoader({}),
            undefined=DebugUndefined,  # Show undefined variables
            auto_reload=True           # Reload templates on changes
        )
    
    def render_with_debug(self, template_source, **context):
        """
        Render template with detailed debugging information.
        
        Parameters:
            template_source: Template source code
            **context: Template context variables
            
        Returns:
            str: Rendered template or detailed error page
        """
        try:
            template = self.env.from_string(template_source)
            return template.render(**context)
            
        except TemplateSyntaxError as e:
            return self._format_syntax_error(e, template_source)
            
        except TemplateRuntimeError as e:
            return self._format_runtime_error(e, template_source, context)
            
        except Exception as e:
            return self._format_generic_error(e, template_source, context)
    
    def _format_syntax_error(self, error, source):
        """Format syntax error with source context."""
        lines = source.split('\n')
        error_line = error.lineno - 1 if error.lineno else 0
        
        context_lines = []
        for i, line in enumerate(lines):
            line_num = i + 1
            marker = ' >>> ' if i == error_line else '     '
            context_lines.append(f'{marker}{line_num:3d}: {html.escape(line)}')
        
        return f'''
        <h2>Template Syntax Error</h2>
        <p><strong>Error:</strong> {html.escape(str(error))}</p>
        <p><strong>Line:</strong> {error.lineno}</p>
        <h3>Template Source:</h3>
        <pre>{"".join(context_lines)}</pre>
        '''
    
    def _format_runtime_error(self, error, source, context):
        """Format runtime error with context information."""
        tb = traceback.format_exc()
        
        return f'''
        <h2>Template Runtime Error</h2>
        <p><strong>Error:</strong> {html.escape(str(error))}</p>
        <p><strong>Line:</strong> {error.lineno}</p>
        <h3>Context Variables:</h3>
        <pre>{html.escape(str(context))}</pre>
        <h3>Template Source:</h3>
        <pre>{html.escape(source)}</pre>
        <h3>Full Traceback:</h3>
        <pre>{html.escape(tb)}</pre>
        '''
    
    def _format_generic_error(self, error, source, context):
        """Format generic error with all available information."""
        tb = traceback.format_exc()
        
        return f'''
        <h2>Template Error</h2>
        <p><strong>Error Type:</strong> {type(error).__name__}</p>
        <p><strong>Error:</strong> {html.escape(str(error))}</p>
        <h3>Context Variables:</h3>
        <pre>{html.escape(str(context))}</pre>
        <h3>Template Source:</h3>
        <pre>{html.escape(source)}</pre>
        <h3>Full Traceback:</h3>
        <pre>{html.escape(tb)}</pre>
        '''

# Usage
dev_renderer = DevelopmentTemplateRenderer()
result = dev_renderer.render_with_debug(
    'Hello {{ user.name }}! You have {{ user.messages | lenght }} messages.',
    user={'name': 'Alice'}
)
```

### Template Validation

Validate templates before rendering to catch errors early:

```python
from jinja2 import Environment, TemplateSyntaxError
from jinja2.meta import find_undeclared_variables
import ast

class TemplateValidator:
    def __init__(self, env):
        self.env = env
    
    def validate_template(self, source, required_vars=None, available_vars=None):
        """
        Validate template syntax and variable usage.
        
        Parameters:
            source: Template source code
            required_vars: Set of variables that must be used
            available_vars: Set of variables that are available
            
        Returns:
            dict: Validation results with errors and warnings
        """
        results = {
            'valid': True,
            'errors': [],
            'warnings': [],
            'undeclared_vars': set(),
            'syntax_ok': True
        }
        
        # Check syntax
        try:
            ast_tree = self.env.parse(source)
            results['syntax_ok'] = True
        except TemplateSyntaxError as e:
            results['valid'] = False
            results['syntax_ok'] = False
            results['errors'].append(f'Syntax error at line {e.lineno}: {e.message}')
            return results  # Can't continue without valid syntax
        
        # Find undeclared variables
        try:
            undeclared = find_undeclared_variables(ast_tree)
            results['undeclared_vars'] = undeclared
            
            # Check against available variables
            if available_vars is not None:
                missing_vars = undeclared - set(available_vars)
                if missing_vars:
                    results['warnings'].extend([
                        f'Undeclared variable: {var}' for var in missing_vars
                    ])
            
            # Check required variables are used
            if required_vars is not None:
                unused_required = set(required_vars) - undeclared
                if unused_required:
                    results['warnings'].extend([
                        f'Required variable not used: {var}' for var in unused_required
                    ])
                    
        except Exception as e:
            results['errors'].append(f'Variable analysis failed: {e}')
        
        return results
    
    def validate_template_file(self, template_name, **validation_kwargs):
        """Validate template loaded from file."""
        try:
            template = self.env.get_template(template_name)
            return self.validate_template(template.source, **validation_kwargs)
        except Exception as e:
            return {
                'valid': False,
                'errors': [f'Failed to load template {template_name}: {e}'],
                'warnings': [],
                'undeclared_vars': set(),
                'syntax_ok': False
            }

# Usage
from jinja2 import FileSystemLoader

env = Environment(loader=FileSystemLoader('templates'))
validator = TemplateValidator(env)

# Validate template source
results = validator.validate_template(
    'Hello {{ user.name }}! You have {{ user.message_count }} messages.',
    available_vars=['user'],
    required_vars=['user']
)

if results['valid']:
    print('Template is valid')
else:
    print('Template validation failed:')
    for error in results['errors']:
        print(f'  ERROR: {error}')
    for warning in results['warnings']:
        print(f'  WARNING: {warning}')
```

## Types

```python { .api }
class ErrorContext:
    """
    Context information for template errors.
    
    Attributes:
        template_name: Name of template where error occurred
        line_number: Line number of error
        column_number: Column number of error (if available)
        source_line: Source code line where error occurred
        context_vars: Template context variables at time of error
        stack_trace: Full stack trace information
    """

class DebugInfo:
    """
    Debug information for template execution.
    
    Attributes:
        template_name: Template name
        execution_time: Template rendering time
        context_size: Size of template context
        undefined_vars: List of undefined variables accessed
        filter_calls: List of filters called during rendering
        include_chain: Chain of included/extended templates
    """
```