"""Custom exception hierarchy and error code mapping. Defines exception hierarchy for adjustment file pipeline, distinguishing between permanent (non-retriable) and transient (retriable) errors for Lambda retry control. Error Types: - PermanentError: Batch marked as ERROR, no retry (validation, missing data, logic errors) - TransientError: Lambda retries with backoff (network, throttling, temporary failures) - ERROR_CODE_MAP: Maps exceptions to BatchErrorCode for database tracking """ from src.enums import BatchErrorCode class PermanentError(Exception): """Non-retriable errors that cause batch to be marked as ERROR.""" class ChecksumMismatchError(PermanentError): """Raised when file checksum verification fails.""" class EmptyFileError(PermanentError): """Raised when the file is empty (no data rows).""" class FileParsingError(PermanentError): """Raised when the file cannot be parsed or processed.""" class FileSizeExceededError(PermanentError): """Raised when the file exceeds the maximum file size.""" class FileIntegrityError(PermanentError): """Raised when downloaded file fails integrity verification.""" class FileSystemError(PermanentError): """Raised when file system operations fail on downloaded file.""" class InvalidFileTypeError(PermanentError): """Raised when the file type is not supported.""" class MissingHeadersError(PermanentError): """Raised when the file is missing required headers.""" class RowCountExceededError(PermanentError): """Raised when the file exceeds the maximum row count.""" class S3FileNotFoundError(PermanentError): """Raised when S3 object is not found.""" class UpdateBatchError(PermanentError): """Raised when the batch is not in the expected state or does not exist.""" class TransientError(Exception): """Retriable errors that trigger Lambda automatic retry with backoff.""" ERROR_CODE_MAP: dict[type[Exception], BatchErrorCode] = { ChecksumMismatchError: BatchErrorCode.CHECKSUM_MISMATCH, EmptyFileError: BatchErrorCode.EMPTY_FILE, S3FileNotFoundError: BatchErrorCode.FILE_NOT_FOUND, FileParsingError: BatchErrorCode.FILE_PARSING_ERROR, FileSizeExceededError: BatchErrorCode.FILE_SIZE_EXCEEDED, FileIntegrityError: BatchErrorCode.FILE_INTEGRITY_ERROR, FileSystemError: BatchErrorCode.FILE_SYSTEM_ERROR, InvalidFileTypeError: BatchErrorCode.INVALID_FILE_TYPE, MissingHeadersError: BatchErrorCode.MISSING_HEADERS, RowCountExceededError: BatchErrorCode.ROW_COUNT_EXCEEDED, UpdateBatchError: BatchErrorCode.BATCH_STATE_ERROR, } def get_error_code(error: Exception) -> BatchErrorCode: """Map exception to BatchErrorCode, returning UNKNOWN_ERROR for unmapped exceptions. Args: error: The exception to map. Returns: The corresponding batch error code. """ return ERROR_CODE_MAP.get(type(error), BatchErrorCode.UNKNOWN_ERROR)