diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index 2b43459132..11c349b7ce 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -34,6 +34,7 @@ TransactionSource, ) from sentry_sdk.tracing_utils import ( + Baggage, add_http_breadcrumb, add_http_request_source, has_span_streaming_enabled, @@ -488,6 +489,19 @@ async def on_request_start( if SENTRY_TRACE_HEADER_NAME in params.headers: headers_to_skip.add(SENTRY_TRACE_HEADER_NAME) + # `baggage` with Sentry members (e.g. merged by the boto3 integration + # before signing): skip so Sentry members are not duplicated. + with capture_internal_exceptions(): + baggage = params.headers.get(BAGGAGE_HEADER_NAME) + if baggage: + if isinstance(baggage, bytes): + baggage = baggage.decode("latin-1") + if any( + Baggage.SENTRY_PREFIX_REGEX.match(item.strip()) + for item in baggage.split(",") + ): + headers_to_skip.add(BAGGAGE_HEADER_NAME) + with capture_internal_exceptions(): authorization = params.headers.get("Authorization") if authorization: diff --git a/tests/integrations/aiohttp/test_aiohttp.py b/tests/integrations/aiohttp/test_aiohttp.py index df8266da21..701ea7d889 100644 --- a/tests/integrations/aiohttp/test_aiohttp.py +++ b/tests/integrations/aiohttp/test_aiohttp.py @@ -939,8 +939,21 @@ async def handler(request): @pytest.mark.asyncio -async def test_outgoing_trace_headers_preserves_signed_propagation_headers( - sentry_init, aiohttp_raw_server, aiohttp_client +@pytest.mark.parametrize( + "signed_headers,baggage", + [ + # both propagation headers are named in `SignedHeaders`. + ("baggage;host;sentry-trace", "vendor=value"), + # botocore 1.43.115+ never signs `baggage`; Sentry members were merged by + # the boto3 integration before signing. + ( + "host;sentry-trace", + "vendor=value,sentry-trace_id=existing,sentry-sampled=true", + ), + ], +) +async def test_outgoing_trace_headers_preserves_existing_propagation_headers( + sentry_init, aiohttp_raw_server, aiohttp_client, signed_headers, baggage ): sentry_init( integrations=[AioHttpIntegration()], @@ -951,11 +964,10 @@ async def handler(request): return web.Response(text="OK") raw_server = await aiohttp_raw_server(handler) - # both propagation headers are named in `SignedHeaders`. authorization = ( "AWS4-HMAC-SHA256 " "Credential=test/20260804/eu-west-1/secretsmanager/aws4_request, " - "SignedHeaders=baggage;host;sentry-trace, " + f"SignedHeaders={signed_headers}, " "Signature=sixtyseven" ) @@ -964,15 +976,15 @@ async def handler(request): resp = await client.get( "/", headers={ - "baggage": "vendor=value", + "baggage": baggage, "sentry-trace": "existing-trace", "Authorization": authorization, }, ) headers = resp.request_info.headers - # signed `baggage`: leave as-is. - assert headers["baggage"] == "vendor=value" + # signed `baggage` or `baggage` with Sentry members: leave as-is. + assert headers["baggage"] == baggage # signed `sentry-trace`: leave as-is. assert headers["sentry-trace"] == "existing-trace"