Skip to content

Commit 7d7860e

Browse files
committed
chore: sanitize connector builder error messages
1 parent a1428bf commit 7d7860e

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

airbyte_cdk/connector_builder/connector_builder_handler.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,15 @@ def read_stream(
117117
),
118118
)
119119
except Exception as exc:
120+
# - message: user-friendly error for display
121+
# - internal_message: technical details for debugging (including config/catalog)
120122
error = AirbyteTracedException.from_exception(
121123
exc,
122-
message=filter_secrets(
123-
f"Error reading stream with config={config} and catalog={configured_catalog}: {str(exc)}"
124-
),
124+
message=filter_secrets(f"Error reading stream: {str(exc)}"),
125+
)
126+
# Override internal_message to include context for debugging
127+
error.internal_message = filter_secrets(
128+
f"Error reading stream with config={config} and catalog={configured_catalog}: {str(exc)}"
125129
)
126130
return error.as_airbyte_message()
127131

unit_tests/connector_builder/test_connector_builder_handler.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
14111477
def 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

Comments
 (0)