From 301520711114eaadb229da6573d7fee4e8f0e7c0 Mon Sep 17 00:00:00 2001 From: serhiy-bzhezytskyy Date: Tue, 18 Aug 2026 17:00:19 +0300 Subject: [PATCH 1/5] Enforce baggage limits on the Jaeger extract path The Jaeger propagator extracts every uberctx- header into baggage with no bound: _extract_baggage walks all carrier keys and sets each one, so an inbound carrier decides how much baggage enters the context. The Jaeger format defines no limits, so this borrows the W3C Baggage spec limits (180 entries, 4096 bytes per entry, 8192 bytes total, byte-denominated), dropping members once a limit is reached and keeping the earlier ones. opentelemetry-java caps its Jaeger propagator the same way for the same reason (CVE-2026-45292). Adds four extract tests: entry count, per-entry bytes, per-entry bytes measured in bytes rather than characters, and the total-bytes cap. Each fails without the change. Assisted-By: Claude Fable 5 --- .../propagators/jaeger/__init__.py | 19 +++++++++- .../tests/test_jaeger_propagator.py | 38 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py index 04a3e8ff5e6..75b8b1a41fb 100644 --- a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py +++ b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py @@ -26,6 +26,11 @@ class JaegerPropagator(TextMapPropagator): TRACE_ID_KEY = "uber-trace-id" BAGGAGE_PREFIX = "uberctx-" DEBUG_FLAG = 0x02 + # The Jaeger format defines no baggage limits, so the W3C Baggage spec + # limits are borrowed to bound an unbounded inbound carrier on extract. + MAX_BAGGAGE_ENTRIES = 180 + MAX_BAGGAGE_ENTRY_BYTES = 4096 + MAX_BAGGAGE_TOTAL_BYTES = 8192 def extract( self, @@ -105,15 +110,27 @@ def _extract_baggage( context: Context, ) -> Context: baggage_keys = [key for key in getter.keys(carrier) if key.startswith(self.BAGGAGE_PREFIX)] + entries = 0 + total_bytes = 0 for key in baggage_keys: + if entries >= self.MAX_BAGGAGE_ENTRIES: + break value = _extract_first_element(getter.get(carrier, key)) if value is None: continue + baggage_key = key.replace(self.BAGGAGE_PREFIX, "") + # The limits are byte-denominated, so a multibyte value cannot + # exceed the budget under a smaller character count. + entry_bytes = len(baggage_key.encode()) + len(value.encode()) + if entry_bytes > self.MAX_BAGGAGE_ENTRY_BYTES or total_bytes + entry_bytes > self.MAX_BAGGAGE_TOTAL_BYTES: + continue context = baggage.set_baggage( - key.replace(self.BAGGAGE_PREFIX, ""), + baggage_key, urllib.parse.unquote(value).strip(), context=context, ) + entries += 1 + total_bytes += entry_bytes return context diff --git a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py index bca14d4de75..10dcd69dfa7 100644 --- a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py +++ b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py @@ -134,6 +134,44 @@ def test_extract_empty_baggage_value(self): context = FORMAT.extract(old_carrier) self.assertDictEqual({"key3": "value3"}, context[_BAGGAGE_KEY]) + def test_extract_enforces_max_baggage_entries(self): + old_carrier = {FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id} + for i in range(200): + old_carrier[f"uberctx-k{i}"] = f"v{i}" + extracted = FORMAT.extract(old_carrier)[_BAGGAGE_KEY] + self.assertEqual(FORMAT.MAX_BAGGAGE_ENTRIES, len(extracted)) + self.assertIn("k0", extracted) + self.assertNotIn("k180", extracted) + + def test_extract_drops_oversized_baggage_entry(self): + old_carrier = { + FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id, + "uberctx-ok": "value", + "uberctx-big": "x" * 5000, + } + context = FORMAT.extract(old_carrier) + self.assertDictEqual({"ok": "value"}, context[_BAGGAGE_KEY]) + + def test_extract_measures_entry_limit_in_bytes(self): + # 2100 multibyte characters is 4200 bytes: under the character limit, + # over the byte limit, so the entry must be dropped. + old_carrier = { + FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id, + "uberctx-ok": "value", + "uberctx-u": "é" * 2100, + } + context = FORMAT.extract(old_carrier) + self.assertDictEqual({"ok": "value"}, context[_BAGGAGE_KEY]) + + def test_extract_enforces_max_baggage_total_bytes(self): + old_carrier = {FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id} + for i in range(100): + old_carrier[f"uberctx-k{i}"] = "y" * 200 + extracted = FORMAT.extract(old_carrier)[_BAGGAGE_KEY] + self.assertLess(len(extracted), 100) + self.assertIn("k0", extracted) + self.assertNotIn("k99", extracted) + def test_extract_invalid_uber_trace_id(self): old_carrier = { "uber-trace-id": "000000000000000000000000deadbeef:00000000deadbef0:00", From 444d6c88283af8047361e79f9706dcb174e8cafd Mon Sep 17 00:00:00 2001 From: serhiy-bzhezytskyy Date: Tue, 18 Aug 2026 17:48:05 +0300 Subject: [PATCH 2/5] Add changelog fragment for #5556 Assisted-By: Claude Fable 5 --- .changelog/5556.fixed | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changelog/5556.fixed diff --git a/.changelog/5556.fixed b/.changelog/5556.fixed new file mode 100644 index 00000000000..a91cd2c09cf --- /dev/null +++ b/.changelog/5556.fixed @@ -0,0 +1 @@ +`opentelemetry-propagator-jaeger`: enforce baggage limits when extracting `uberctx-` headers, borrowing the W3C Baggage spec limits (180 entries, 4096 bytes per entry, 8192 bytes total) so an inbound carrier cannot add unbounded baggage to the context. From beee0f36fd091e83f0b7bbbfe596684cd1191a25 Mon Sep 17 00:00:00 2001 From: serhiy-bzhezytskyy Date: Wed, 19 Aug 2026 00:51:40 +0300 Subject: [PATCH 3/5] Bound the extract work and count the separator bytes Two points from review. The entry limit bounded the entries kept, not the candidates inspected, so a carrier full of oversized uberctx- headers could force unbounded decoding and byte accounting; the limit now bounds the candidates via itertools.islice, which also makes the accepted-entry counter redundant since kept is at most inspected. opentelemetry-java bounds its jaeger baggage parse the same way, in MAX_BAGGAGE_HEADER_TOKENS. The per-entry accounting also omitted the "=" and "," a baggage header carries, so each entry now counts two more bytes. That makes the numbers slightly stricter than opentelemetry-java, which counts key and value only. Assisted-By: Claude Fable 5 --- .../propagators/jaeger/__init__.py | 19 +++++++----- .../tests/test_jaeger_propagator.py | 29 +++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py index 75b8b1a41fb..18043adaa2a 100644 --- a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py +++ b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 import collections.abc +import itertools import urllib.parse from opentelemetry import baggage, trace @@ -109,19 +110,22 @@ def _extract_baggage( carrier: CarrierT, context: Context, ) -> Context: - baggage_keys = [key for key in getter.keys(carrier) if key.startswith(self.BAGGAGE_PREFIX)] - entries = 0 + # The limit bounds the candidates inspected, not the entries kept, so a + # carrier full of oversized ones cannot force unbounded work. + candidates = itertools.islice( + (key for key in getter.keys(carrier) if key.startswith(self.BAGGAGE_PREFIX)), + self.MAX_BAGGAGE_ENTRIES, + ) total_bytes = 0 - for key in baggage_keys: - if entries >= self.MAX_BAGGAGE_ENTRIES: - break + for key in candidates: value = _extract_first_element(getter.get(carrier, key)) if value is None: continue baggage_key = key.replace(self.BAGGAGE_PREFIX, "") # The limits are byte-denominated, so a multibyte value cannot - # exceed the budget under a smaller character count. - entry_bytes = len(baggage_key.encode()) + len(value.encode()) + # exceed the budget under a smaller character count. The two extra + # bytes account for the "=" and "," a W3C baggage header would carry. + entry_bytes = len(baggage_key.encode()) + len(value.encode()) + 2 if entry_bytes > self.MAX_BAGGAGE_ENTRY_BYTES or total_bytes + entry_bytes > self.MAX_BAGGAGE_TOTAL_BYTES: continue context = baggage.set_baggage( @@ -129,7 +133,6 @@ def _extract_baggage( urllib.parse.unquote(value).strip(), context=context, ) - entries += 1 total_bytes += entry_bytes return context diff --git a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py index 10dcd69dfa7..bd9201503fe 100644 --- a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py +++ b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py @@ -10,6 +10,7 @@ from opentelemetry.propagators import ( # pylint: disable=no-name-in-module jaeger, ) +from opentelemetry.propagators.textmap import DefaultGetter from opentelemetry.sdk import trace from opentelemetry.sdk.trace import id_generator from opentelemetry.test import TestCase @@ -172,6 +173,34 @@ def test_extract_enforces_max_baggage_total_bytes(self): self.assertIn("k0", extracted) self.assertNotIn("k99", extracted) + def test_extract_counts_the_separator_bytes(self): + # key + value is 4095 bytes, one under the per-entry limit, but the + # "=" and "," a baggage header would carry put it one over. + old_carrier = { + FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id, + "uberctx-ok": "value", + "uberctx-edge": "x" * (FORMAT.MAX_BAGGAGE_ENTRY_BYTES - len("edge") - 1), + } + context = FORMAT.extract(old_carrier) + self.assertDictEqual({"ok": "value"}, context[_BAGGAGE_KEY]) + + def test_extract_stops_inspecting_after_the_candidate_limit(self): + old_carrier = {FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id} + for index in range(1000): + old_carrier[f"uberctx-k{index}"] = "x" * 5000 + + class CountingGetter(DefaultGetter): + def __init__(self): + self.reads = 0 + + def get(self, carrier, key): + self.reads += 1 + return super().get(carrier, key) + + getter = CountingGetter() + FORMAT.extract(old_carrier, getter=getter) + self.assertLessEqual(getter.reads, FORMAT.MAX_BAGGAGE_ENTRIES + 1) + def test_extract_invalid_uber_trace_id(self): old_carrier = { "uber-trace-id": "000000000000000000000000deadbeef:00000000deadbef0:00", From c1a80fc3780cea9bfdc7c1ea5333ed505b10b474 Mon Sep 17 00:00:00 2001 From: serhiy-bzhezytskyy Date: Wed, 19 Aug 2026 07:17:14 +0300 Subject: [PATCH 4/5] Keep the lint gate clean for the added tests The tests added here take the class past pylint's max-public-methods of 20, which fails `lint-opentelemetry-propagator-jaeger`. Disable that check on the class, the same way `propagator/opentelemetry-propagator-b3/tests/test_b3_format.py` does, and rename the loop variables the new tests introduced so `invalid-name` stays quiet without a second suppression. Assisted-By: Claude Fable 5 --- .../tests/test_jaeger_propagator.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py index bd9201503fe..0767f4aec31 100644 --- a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py +++ b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py @@ -47,6 +47,8 @@ def get_context_new_carrier(old_carrier, carrier_baggage=None): class TestJaegerPropagator(TestCase): + # pylint: disable=too-many-public-methods + @classmethod def setUpClass(cls): generator = id_generator.RandomIdGenerator() @@ -137,8 +139,8 @@ def test_extract_empty_baggage_value(self): def test_extract_enforces_max_baggage_entries(self): old_carrier = {FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id} - for i in range(200): - old_carrier[f"uberctx-k{i}"] = f"v{i}" + for index in range(200): + old_carrier[f"uberctx-k{index}"] = f"v{index}" extracted = FORMAT.extract(old_carrier)[_BAGGAGE_KEY] self.assertEqual(FORMAT.MAX_BAGGAGE_ENTRIES, len(extracted)) self.assertIn("k0", extracted) @@ -166,8 +168,8 @@ def test_extract_measures_entry_limit_in_bytes(self): def test_extract_enforces_max_baggage_total_bytes(self): old_carrier = {FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id} - for i in range(100): - old_carrier[f"uberctx-k{i}"] = "y" * 200 + for index in range(100): + old_carrier[f"uberctx-k{index}"] = "y" * 200 extracted = FORMAT.extract(old_carrier)[_BAGGAGE_KEY] self.assertLess(len(extracted), 100) self.assertIn("k0", extracted) From 22409761baaf5876946763efe34f06bfe1dd132e Mon Sep 17 00:00:00 2001 From: serhiy-bzhezytskyy Date: Thu, 20 Aug 2026 20:45:24 +0300 Subject: [PATCH 5/5] fix: address review comments on the jaeger baggage limits Fix the extract-side byte accounting: entry_bytes charged both the "=" and the "," on every entry, but the "," only ever appears between entries. Charge "=" per entry and "," once per already-accepted entry against the running total instead, in a shared _limit_baggage_bytes helper. Cap inject the same way extract is capped, so an in-process baggage map built without going through extract cannot produce an unbounded number or volume of uberctx- headers either. Both paths now build a bounded list of candidate pairs, then filter it through the same byte budget. Update the changelog fragment to cover both directions. --- .changelog/5556.fixed | 2 +- .../propagators/jaeger/__init__.py | 55 +++++++++++++----- .../tests/test_jaeger_propagator.py | 58 ++++++++++++++++--- 3 files changed, 93 insertions(+), 22 deletions(-) diff --git a/.changelog/5556.fixed b/.changelog/5556.fixed index a91cd2c09cf..d8bb2ca6631 100644 --- a/.changelog/5556.fixed +++ b/.changelog/5556.fixed @@ -1 +1 @@ -`opentelemetry-propagator-jaeger`: enforce baggage limits when extracting `uberctx-` headers, borrowing the W3C Baggage spec limits (180 entries, 4096 bytes per entry, 8192 bytes total) so an inbound carrier cannot add unbounded baggage to the context. +`opentelemetry-propagator-jaeger`: enforce baggage limits on both `uberctx-` extract and inject, borrowing the same limits (180 entries, 4096 bytes per entry, 8192 bytes total) the package's core `W3CBaggagePropagator` already uses, so neither an inbound carrier nor an in-process baggage map can produce unbounded work or headers. diff --git a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py index 18043adaa2a..08c6e99de80 100644 --- a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py +++ b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py @@ -96,9 +96,7 @@ def inject( baggage_entries = baggage.get_all(context=context) if not baggage_entries: return - for key, value in baggage_entries.items(): - baggage_key = self.BAGGAGE_PREFIX + key - setter.set(carrier, baggage_key, urllib.parse.quote(str(value))) + self._inject_baggage(setter, carrier, baggage_entries) @property def fields(self) -> set[str]: @@ -116,26 +114,55 @@ def _extract_baggage( (key for key in getter.keys(carrier) if key.startswith(self.BAGGAGE_PREFIX)), self.MAX_BAGGAGE_ENTRIES, ) - total_bytes = 0 + pairs = [] for key in candidates: value = _extract_first_element(getter.get(carrier, key)) - if value is None: - continue - baggage_key = key.replace(self.BAGGAGE_PREFIX, "") - # The limits are byte-denominated, so a multibyte value cannot - # exceed the budget under a smaller character count. The two extra - # bytes account for the "=" and "," a W3C baggage header would carry. - entry_bytes = len(baggage_key.encode()) + len(value.encode()) + 2 - if entry_bytes > self.MAX_BAGGAGE_ENTRY_BYTES or total_bytes + entry_bytes > self.MAX_BAGGAGE_TOTAL_BYTES: - continue + if value is not None: + pairs.append((key.replace(self.BAGGAGE_PREFIX, ""), value)) + + for baggage_key, value in _limit_baggage_bytes( + pairs, self.MAX_BAGGAGE_ENTRY_BYTES, self.MAX_BAGGAGE_TOTAL_BYTES + ): context = baggage.set_baggage( baggage_key, urllib.parse.unquote(value).strip(), context=context, ) - total_bytes += entry_bytes return context + def _inject_baggage( + self, + setter: Setter[CarrierT], + carrier: CarrierT, + baggage_entries: collections.abc.Mapping[str, object], + ) -> None: + candidates = itertools.islice(baggage_entries.items(), self.MAX_BAGGAGE_ENTRIES) + pairs = [(key, urllib.parse.quote(str(value))) for key, value in candidates] + + for key, encoded_value in _limit_baggage_bytes( + pairs, self.MAX_BAGGAGE_ENTRY_BYTES, self.MAX_BAGGAGE_TOTAL_BYTES + ): + setter.set(carrier, self.BAGGAGE_PREFIX + key, encoded_value) + + +def _limit_baggage_bytes( + pairs: collections.abc.Iterable[tuple[str, str]], + max_entry_bytes: int, + max_total_bytes: int, +) -> collections.abc.Iterator[tuple[str, str]]: + total_bytes = 0 + accepted = 0 + for key, value in pairs: + entry_bytes = len(key.encode()) + len(value.encode()) + 1 + if entry_bytes > max_entry_bytes: + continue + separator_bytes = 1 if accepted > 0 else 0 + if total_bytes + separator_bytes + entry_bytes > max_total_bytes: + continue + yield key, value + total_bytes += separator_bytes + entry_bytes + accepted += 1 + def _format_uber_trace_id(trace_id, span_id, parent_span_id, flags): return f"{format_trace_id(trace_id)}:{format_span_id(span_id)}:{format_span_id(parent_span_id)}:{flags:02x}" diff --git a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py index 0767f4aec31..0db7cf593b6 100644 --- a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py +++ b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py @@ -175,16 +175,33 @@ def test_extract_enforces_max_baggage_total_bytes(self): self.assertIn("k0", extracted) self.assertNotIn("k99", extracted) - def test_extract_counts_the_separator_bytes(self): - # key + value is 4095 bytes, one under the per-entry limit, but the - # "=" and "," a baggage header would carry put it one over. + def test_extract_counts_the_key_value_separator_byte(self): old_carrier = { FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id, - "uberctx-ok": "value", - "uberctx-edge": "x" * (FORMAT.MAX_BAGGAGE_ENTRY_BYTES - len("edge") - 1), + "uberctx-fits": "x" * (FORMAT.MAX_BAGGAGE_ENTRY_BYTES - len("fits") - 1), + "uberctx-over": "x" * (FORMAT.MAX_BAGGAGE_ENTRY_BYTES - len("over")), } - context = FORMAT.extract(old_carrier) - self.assertDictEqual({"ok": "value"}, context[_BAGGAGE_KEY]) + extracted = FORMAT.extract(old_carrier)[_BAGGAGE_KEY] + self.assertIn("fits", extracted) + self.assertNotIn("over", extracted) + + def test_extract_charges_a_separator_byte_between_accepted_entries(self): + small_entry_bytes = len("small") + 1 # empty value + filler_a_entry_bytes = FORMAT.MAX_BAGGAGE_ENTRY_BYTES + filler_a_value_len = filler_a_entry_bytes - len("filler_a") - 1 + target_total_after_fillers = FORMAT.MAX_BAGGAGE_TOTAL_BYTES - small_entry_bytes + filler_b_entry_bytes = target_total_after_fillers - filler_a_entry_bytes - 1 + filler_b_value_len = filler_b_entry_bytes - len("filler_b") - 1 + old_carrier = { + FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id, + "uberctx-filler_a": "x" * filler_a_value_len, + "uberctx-filler_b": "x" * filler_b_value_len, + "uberctx-small": "", + } + extracted = FORMAT.extract(old_carrier)[_BAGGAGE_KEY] + self.assertIn("filler_a", extracted) + self.assertIn("filler_b", extracted) + self.assertNotIn("small", extracted) def test_extract_stops_inspecting_after_the_candidate_limit(self): old_carrier = {FORMAT.TRACE_ID_KEY: self.serialized_uber_trace_id} @@ -203,6 +220,33 @@ def get(self, carrier, key): FORMAT.extract(old_carrier, getter=getter) self.assertLessEqual(getter.reads, FORMAT.MAX_BAGGAGE_ENTRIES + 1) + def test_inject_enforces_max_baggage_entries(self): + span = trace_api.NonRecordingSpan(trace_api.SpanContext(1, 1, True)) + ctx = trace_api.set_span_in_context(span) + for index in range(200): + ctx = baggage.set_baggage(f"k{index}", f"v{index}", ctx) + + carrier = {} + FORMAT.inject(carrier, context=ctx) + + self.assertEqual(FORMAT.MAX_BAGGAGE_ENTRIES, sum(1 for key in carrier if key.startswith(FORMAT.BAGGAGE_PREFIX))) + self.assertIn(FORMAT.BAGGAGE_PREFIX + "k0", carrier) + self.assertNotIn(FORMAT.BAGGAGE_PREFIX + "k180", carrier) + + def test_inject_enforces_max_baggage_total_bytes(self): + span = trace_api.NonRecordingSpan(trace_api.SpanContext(1, 1, True)) + ctx = trace_api.set_span_in_context(span) + for index in range(100): + ctx = baggage.set_baggage(f"k{index}", "y" * 200, ctx) + + carrier = {} + FORMAT.inject(carrier, context=ctx) + + injected = [key for key in carrier if key.startswith(FORMAT.BAGGAGE_PREFIX)] + self.assertLess(len(injected), 100) + self.assertIn(FORMAT.BAGGAGE_PREFIX + "k0", carrier) + self.assertNotIn(FORMAT.BAGGAGE_PREFIX + "k99", carrier) + def test_extract_invalid_uber_trace_id(self): old_carrier = { "uber-trace-id": "000000000000000000000000deadbeef:00000000deadbef0:00",