@@ -2478,6 +2478,22 @@ def _braintrust_parent_to_components(braintrust_parent: str):
24782478 return None
24792479
24802480
2481+ def _set_header (carrier : dict , name : str , value : str ) -> None :
2482+ """Set a W3C trace-context header on a carrier, sending the lowercase name.
2483+
2484+ Per the W3C Trace Context spec (§3.2.1 / §3.3.1), vendors SHOULD send these
2485+ header names in lowercase. ``name`` is always the canonical lowercase key.
2486+ A plain ``dict`` carrier is case-sensitive, so any pre-existing case-variant
2487+ (e.g. ``Baggage`` from a framework that title-cases headers) must be removed
2488+ first, otherwise the carrier would end up with two conflicting headers.
2489+ """
2490+ lowered = name .lower ()
2491+ for key in list (carrier .keys ()):
2492+ if isinstance (key , str ) and key != name and key .lower () == lowered :
2493+ del carrier [key ]
2494+ carrier [name ] = value
2495+
2496+
24812497def _inject_into_carrier (
24822498 carrier : dict ,
24832499 trace_id : str ,
@@ -2496,11 +2512,11 @@ def _inject_into_carrier(
24962512 if traceparent is None :
24972513 # Ids aren't W3C-shaped (e.g. legacy UUID mode); nothing to propagate.
24982514 return
2499- carrier [ TRACEPARENT_HEADER ] = traceparent
2515+ _set_header ( carrier , TRACEPARENT_HEADER , traceparent )
25002516
25012517 # Forward upstream tracestate (per W3C, only alongside a valid traceparent).
25022518 if tracestate :
2503- carrier [ TRACESTATE_HEADER ] = tracestate
2519+ _set_header ( carrier , TRACESTATE_HEADER , tracestate )
25042520
25052521 # Merge braintrust.parent into any existing baggage, preserving other keys.
25062522 existing = get_header (carrier , BAGGAGE_HEADER )
@@ -2509,7 +2525,7 @@ def _inject_into_carrier(
25092525 entries [BRAINTRUST_PARENT_KEY ] = braintrust_parent
25102526 baggage_value = format_baggage (entries )
25112527 if baggage_value is not None :
2512- carrier [ BAGGAGE_HEADER ] = baggage_value
2528+ _set_header ( carrier , BAGGAGE_HEADER , baggage_value )
25132529
25142530
25152531def inject_trace_context (carrier : dict | None = None , span : "Span | None" = None ) -> dict :
0 commit comments