From e8f8aceada8180b99d8f730887af1a71e4b38e0a Mon Sep 17 00:00:00 2001 From: Safal Gupta <148842438+alphasafal@users.noreply.github.com> Date: Sun, 25 Oct 2026 18:23:19 +0000 Subject: [PATCH 1/2] fix(tracing): drop trailing comma when serializing baggage without third-party items Baggage.serialize(include_third_party=True) appended third_party_items unconditionally, so when there were no third-party items it added an empty segment and the serialized header ended with a comma (e.g. "sentry-trace_id=...,sentry-public_key=...,"). This is reachable from the Celery integration, which calls combined.serialize(include_third_party=True) whenever the merged baggage has only Sentry items. Only append third_party_items when it is non-empty; the output is unchanged when third-party items are present. Adds a regression test. --- sentry_sdk/tracing_utils.py | 2 +- tests/tracing/test_baggage.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 8a6961ba15..2ac3a67e4b 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -973,7 +973,7 @@ def serialize(self, include_third_party: bool = False) -> str: item = Baggage.SENTRY_PREFIX + quote(key) + "=" + quote(str(val)) items.append(item) - if include_third_party: + if include_third_party and self.third_party_items: items.append(self.third_party_items) return ",".join(items) diff --git a/tests/tracing/test_baggage.py b/tests/tracing/test_baggage.py index 1e0075feaa..1f06ed0275 100644 --- a/tests/tracing/test_baggage.py +++ b/tests/tracing/test_baggage.py @@ -67,6 +67,30 @@ def test_mixed_baggage(): ) +def test_serialize_no_trailing_comma_without_third_party_items(): + # With Sentry items but no third-party items, include_third_party=True must not + # append an empty segment, which would leave a trailing comma in the header. + header = ( + "sentry-trace_id=771a43a4192642f0b136d5159a501700," + "sentry-public_key=49d0f7386ad645858ae85020e393bef3," + "sentry-sample_rate=0.01337" + ) + baggage = Baggage.from_incoming_header(header) + + assert baggage.third_party_items == "" + + serialized = baggage.serialize(include_third_party=True) + + assert not serialized.endswith(",") + # with no third-party items, include_third_party should change nothing + assert serialized == baggage.serialize() + assert serialized == ( + "sentry-trace_id=771a43a4192642f0b136d5159a501700," + "sentry-public_key=49d0f7386ad645858ae85020e393bef3," + "sentry-sample_rate=0.01337" + ) + + def test_malformed_baggage(): header = "," From 0bc5679eb931bfebde77d4ced83d20de9e6d7f58 Mon Sep 17 00:00:00 2001 From: Safal Gupta <148842438+alphasafal@users.noreply.github.com> Date: Tue, 27 Oct 2026 09:05:47 +0530 Subject: [PATCH 2/2] test(celery): cover baggage propagation without third-party items Adds a Celery header test where the incoming baggage has only Sentry items. The outgoing baggage must not end with a trailing comma. --- .../celery/test_update_celery_task_headers.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/integrations/celery/test_update_celery_task_headers.py b/tests/integrations/celery/test_update_celery_task_headers.py index 18f2f96bbf..0afebc7b98 100644 --- a/tests/integrations/celery/test_update_celery_task_headers.py +++ b/tests/integrations/celery/test_update_celery_task_headers.py @@ -15,6 +15,12 @@ "custom=value" ) +SENTRY_ONLY_BAGGAGE_VALUE = ( + "sentry-trace_id=771a43a4192642f0b136d5159a501700," + "sentry-public_key=49d0f7386ad645858ae85020e393bef3," + "sentry-sample_rate=0.1337" +) + SENTRY_TRACE_VALUE = "771a43a4192642f0b136d5159a501700-1234567890abcdef-1" @@ -124,6 +130,28 @@ def test_span_with_transaction_custom_headers(sentry_init): ) +def test_span_with_transaction_custom_headers_without_third_party_baggage( + sentry_init, +): + sentry_init(traces_sample_rate=1.0) + headers = { + "baggage": SENTRY_ONLY_BAGGAGE_VALUE, + "sentry-trace": SENTRY_TRACE_VALUE, + } + + with sentry_sdk.start_transaction(name="test_transaction"): + with sentry_sdk.start_span(op="test_span") as span: + outgoing_headers = _update_celery_task_headers(headers, span, False) + + for baggage in ( + outgoing_headers["baggage"], + outgoing_headers["headers"]["baggage"], + ): + assert baggage.startswith("sentry-") + assert not baggage.endswith(",") + assert "custom=" not in baggage + + @pytest.mark.parametrize("monitor_beat_tasks", [True, False]) def test_celery_trace_propagation_default(sentry_init, monitor_beat_tasks): """