@@ -1408,6 +1408,72 @@ def test_read_stream_exception_with_secrets():
14081408 assert "super_secret_key" not in response .trace .error .message
14091409
14101410
1411+ def test_read_stream_error_message_does_not_contain_config_and_catalog ():
1412+ """
1413+ Test that error messages in read_stream are clean and user-friendly,
1414+ without embedding verbose config and catalog information.
1415+
1416+ This test verifies that:
1417+ 1. The user-facing `message` is clean and doesn't contain config/catalog dumps
1418+ 2. The technical `internal_message` still contains full context for debugging
1419+ """
1420+ # Create a config and catalog with identifiable content
1421+ config = {
1422+ "__injected_declarative_manifest" : "test_manifest" ,
1423+ "verbose_config_data" : "this_should_not_appear_in_user_message" ,
1424+ "api_key" : "secret_key_value"
1425+ }
1426+ catalog = ConfiguredAirbyteCatalog (
1427+ streams = [
1428+ ConfiguredAirbyteStream (
1429+ stream = AirbyteStream (
1430+ name = _stream_name ,
1431+ json_schema = {"properties" : {"verbose_catalog_schema" : {"type" : "string" }}},
1432+ supported_sync_modes = [SyncMode .full_refresh ]
1433+ ),
1434+ sync_mode = SyncMode .full_refresh ,
1435+ destination_sync_mode = DestinationSyncMode .append ,
1436+ )
1437+ ]
1438+ )
1439+ state = []
1440+ limits = TestLimits ()
1441+
1442+ # Mock the source
1443+ mock_source = MagicMock ()
1444+
1445+ # Patch the handler to raise a meaningful exception
1446+ with patch (
1447+ "airbyte_cdk.connector_builder.test_reader.TestReader.run_test_read"
1448+ ) as mock_handler :
1449+ # Simulate a common error like the datetime parsing error from the user's example
1450+ mock_handler .side_effect = ValueError ("time data '' does not match format '%Y-%m-%dT%H:%M:%SZ'" )
1451+
1452+ # Call the read_stream function
1453+ response = read_stream (mock_source , config , catalog , state , limits )
1454+
1455+ # Verify it's a trace message with an error
1456+ assert response .type == Type .TRACE
1457+ assert response .trace .type .value == "ERROR"
1458+
1459+ # The user-facing message should be clean - no config or catalog dumps
1460+ user_message = response .trace .error .message
1461+ assert "verbose_config_data" not in user_message
1462+ assert "verbose_catalog_schema" not in user_message
1463+ assert "__injected_declarative_manifest" not in user_message
1464+
1465+ # But it should contain the actual error
1466+ assert "time data" in user_message
1467+ assert "does not match format" in user_message
1468+
1469+ # The internal message should contain technical details for debugging
1470+ internal_message = response .trace .error .internal_message
1471+ assert "verbose_config_data" in internal_message
1472+ assert "verbose_catalog_schema" in internal_message
1473+ assert "Error reading stream with config=" in internal_message
1474+ assert "and catalog=" in internal_message
1475+
1476+
14111477def test_full_resolve_manifest (valid_resolve_manifest_config_file ):
14121478 config = copy .deepcopy (RESOLVE_DYNAMIC_STREAM_MANIFEST_CONFIG )
14131479 command = config ["__command" ]
0 commit comments