From d1d0a09c875fe89cb09753d5a11ed9607bb8c940 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:40:10 +0000 Subject: [PATCH] fix(file-based): classify CSV encoding errors as config_error to prevent unnecessary retries The AirbyteTracedException raised for UnicodeError in csv_parser.py was missing failure_type, defaulting to system_error (retriable). This caused up to 5 unnecessary retry attempts for encoding mismatch errors that will never succeed without a config change. - Add failure_type=FailureType.config_error - Add internal_message with file URI, encoding, and original error - Pass exception parameter and chain with 'from e' - Update test to verify all new exception attributes Co-Authored-By: bot_apk --- airbyte_cdk/sources/file_based/file_types/csv_parser.py | 9 ++++++--- .../sources/file_based/file_types/test_csv_parser.py | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/airbyte_cdk/sources/file_based/file_types/csv_parser.py b/airbyte_cdk/sources/file_based/file_types/csv_parser.py index b8c720642a..dfb2c9bfea 100644 --- a/airbyte_cdk/sources/file_based/file_types/csv_parser.py +++ b/airbyte_cdk/sources/file_based/file_types/csv_parser.py @@ -73,10 +73,13 @@ def read_data( headers, raw_headers = self._read_and_validate_headers( fp, config_format, dialect_name ) - except UnicodeError: + except UnicodeError as e: raise AirbyteTracedException( - message=f"{FileBasedSourceError.ENCODING_ERROR.value} Expected encoding: {config_format.encoding}", - ) + message=f"{FileBasedSourceError.ENCODING_ERROR.value} Expected encoding: {config_format.encoding}.", + internal_message=f"UnicodeError reading file {file.uri} with encoding {config_format.encoding}: {e}", + failure_type=FailureType.config_error, + exception=e, + ) from e rows_to_skip = ( config_format.skip_rows_before_header diff --git a/unit_tests/sources/file_based/file_types/test_csv_parser.py b/unit_tests/sources/file_based/file_types/test_csv_parser.py index f12f6613e8..2e5a38286f 100644 --- a/unit_tests/sources/file_based/file_types/test_csv_parser.py +++ b/unit_tests/sources/file_based/file_types/test_csv_parser.py @@ -648,15 +648,18 @@ def test_read_data_with_encoding_error(self) -> None: self._stream_reader.open_file.return_value = ( CsvFileBuilder().with_data(["something"]).build() ) - self._csv_reader._read_and_validate_headers = Mock( - side_effect=UnicodeDecodeError("encoding", b"", 0, 1, "reason") - ) + unicode_error = UnicodeDecodeError("utf-8", b"\x9d", 0, 1, "invalid start byte") + self._csv_reader._read_and_validate_headers = Mock(side_effect=unicode_error) with pytest.raises(AirbyteTracedException) as ate: data_generator = self._read_data() assert len(list(data_generator)) == 0 assert "encoding" in ate.value.message + assert ate.value.failure_type == FailureType.config_error + assert self._file.uri in ate.value.internal_message + assert "utf-8" in ate.value.internal_message + assert ate.value.__cause__ is unicode_error assert self._csv_reader._read_and_validate_headers.called def _read_data(self) -> Generator[Dict[str, str], None, None]: