Errors - PyCharter
Description: Data Contract Management, ETL Pipelines, and Quality Assurance for Python

Skip to content

# Errors

PyCharter's exception hierarchy for error handling.

## Exception Hierarchy

    PyCharterError (base)
    ├── ConfigError
    │   └── ConfigLoadError
    ├── ConfigValidationError
    └── ExpressionError

## PyCharterError

Base exception for all PyCharter errors:

    from pycharter.shared.errors import PyCharterError

    try:
        result = await pipeline.run()
    except PyCharterError as e:
        print(f"PyCharter error: {e}")

## ConfigError

Raised for configuration loading/parsing failures:

    from pycharter.shared.errors import ConfigError

    try:
        pipeline = Pipeline.from_config_dir("invalid/path/")
    except ConfigError as e:
        print(f"Config error: {e}")

## ConfigValidationError

Raised when configuration fails schema validation:

    from pycharter.shared.errors import ConfigValidationError

    try:
        pipeline = Pipeline.from_config_files(
            extract="extract.yaml",  # Missing required 'type' field
            load="load.yaml"
        )
    except ConfigValidationError as e:
        print(f"Validation error: {e}")

## ConfigLoadError

Raised when a config file cannot be loaded:

    from pycharter.shared.errors import ConfigLoadError

    try:
        pipeline = Pipeline.from_config_files(
            extract="nonexistent.yaml",
            load="load.yaml"
        )
    except ConfigLoadError as e:
        print(f"Load error: {e}")

## ExpressionError

Raised when expression evaluation fails:

    from pycharter.shared.errors import ExpressionError

    try:
        transform = AddField("result", "invalid_function()")
    except ExpressionError as e:
        print(f"Expression error: {e}")

## ErrorMode

Control how pipeline errors are handled:

    from pycharter.shared.errors import ErrorMode, ErrorContext

    # STRICT: Raise on first error (default)
    result = await pipeline.run(
        error_context=ErrorContext(mode=ErrorMode.STRICT)
    )

    # LENIENT: Log and continue
    result = await pipeline.run(
        error_context=ErrorContext(mode=ErrorMode.LENIENT)
    )

    # COLLECT: Gather all errors
    result = await pipeline.run(
        error_context=ErrorContext(mode=ErrorMode.COLLECT)
    )
    print(result.errors)

## ErrorContext

## `` ErrorContext `dataclass`

    ErrorContext(
        mode: ErrorMode = LENIENT,
        errors: list[str] = list(),
        warnings: list[str] = list(),
    )

Context for tracking errors during operations.

Use with ErrorMode.COLLECT to gather errors without stopping.

### `` has_errors `property`

    has_errors: bool

Whether any errors were collected.

### `` handle_error

    handle_error(
        message: str,
        exception: Exception | None = None,
        category: str = "error",
    ) -> None

Handle an error according to the current mode.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`message` | `str` | Error message | required
`exception` | `Exception | None` | Optional exception that caused the error | `None`
`category` | `str` | Error category for logging | `'error'`

Source code in `src/pycharter/shared/errors.py`

    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    def handle_error(
        self,
        message: str,
        exception: Exception | None = None,
        category: str = "error",
    ) -> None:
        """
        Handle an error according to the current mode.

        Args:
            message: Error message
            exception: Optional exception that caused the error
            category: Error category for logging
        """
        full_message = f"{category}: {message}"
        if exception:
            full_message += f" ({type(exception).__name__}: {exception})"

        if self.mode == ErrorMode.STRICT:
            if exception:
                raise type(exception)(full_message) from exception
            raise ValueError(full_message)

        elif self.mode == ErrorMode.LENIENT:
            warnings.warn(full_message)
            logger.warning(full_message)
            self.warnings.append(full_message)

        elif self.mode == ErrorMode.COLLECT:
            self.errors.append(full_message)
            logger.warning(full_message)

### `` handle_warning

    handle_warning(
        message: str, category: str = "warning"
    ) -> None

Handle a warning (non-fatal issue).

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`message` | `str` | Warning message | required
`category` | `str` | Warning category | `'warning'`

Source code in `src/pycharter/shared/errors.py`

    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    def handle_warning(self, message: str, category: str = "warning") -> None:
        """
        Handle a warning (non-fatal issue).

        Args:
            message: Warning message
            category: Warning category
        """
        full_message = f"{category}: {message}"
        warnings.warn(full_message)
        logger.warning(full_message)
        self.warnings.append(full_message)

### `` raise_if_errors

    raise_if_errors() -> None

Raise ValueError if any errors were collected.

Source code in `src/pycharter/shared/errors.py`

    205
    206
    207
    208
    def raise_if_errors(self) -> None:
        """Raise ValueError if any errors were collected."""
        if self.errors:
            raise ValueError(f"Errors occurred: {'; '.join(self.errors)}")

### `` clear

    clear() -> None

Clear all errors and warnings.

Source code in `src/pycharter/shared/errors.py`

    210
    211
    212
    213
    def clear(self) -> None:
        """Clear all errors and warnings."""
        self.errors.clear()
        self.warnings.clear()

## Best Practices

### Catch Specific Exceptions

    from pycharter.shared.errors import (
        PyCharterError,
        ConfigError,
        ConfigValidationError,
        ExpressionError
    )

    try:
        pipeline = Pipeline.from_config_dir("pipelines/")
        result = await pipeline.run()
    except ConfigValidationError as e:
        # Handle invalid config
        print(f"Invalid configuration: {e}")
    except ConfigError as e:
        # Handle missing/corrupt config
        print(f"Configuration error: {e}")
    except ExpressionError as e:
        # Handle expression errors
        print(f"Expression error: {e}")
    except PyCharterError as e:
        # Catch-all for other PyCharter errors
        print(f"PyCharter error: {e}")

### Use ErrorContext for Batch Processing

    # Collect errors for batch analysis
    error_context = ErrorContext(mode=ErrorMode.COLLECT)
    result = await pipeline.run(error_context=error_context)

    if result.errors:
        # Log all errors
        for error in result.errors:
            logger.error(f"Pipeline error: {error}")

        # Decide whether to fail
        if len(result.errors) > 10:
            raise Exception(f"Too many errors: {len(result.errors)}")