@@ -14,8 +14,8 @@ import { mergeBaggageHeaders } from './baggage';
1414const SENTRY_TRACE_HEADER = 'sentry-trace' ;
1515const SENTRY_BAGGAGE_HEADER = 'baggage' ;
1616
17- // For baggage, we make sure to merge this into a possibly existing header
18- const BAGGAGE_HEADER_REGEX = / b a g g a g e : ( . * ) \r \n / ;
17+ const BAGGAGE_HEADER_REGEX_GLOBAL = / b a g g a g e : ( . * ) \r \n / g ;
18+ const SENTRY_TRACE_HEADER_REGEX_GLOBAL = / s e n t r y - t r a c e : . * \r \n / g ;
1919
2020/**
2121 * Add trace propagation headers to an outgoing fetch/undici request.
@@ -60,15 +60,44 @@ export function addTracePropagationHeadersToFetchRequest(
6060 requestHeaders . push ( 'traceparent' , traceparent ) ;
6161 }
6262
63- // For baggage, we make sure to merge this into a possibly existing header
64- const existingBaggagePos = requestHeaders . findIndex ( header => header === SENTRY_BAGGAGE_HEADER ) ;
65- if ( baggage && existingBaggagePos === - 1 ) {
63+ // Consolidate all duplicate baggage entries into one, then merge with our new baggage.
64+ // OTel's UndiciInstrumentation may append a second baggage header via propagation.inject(),
65+ // so we need to handle multiple entries — not just the first one.
66+ const baggagePositions : number [ ] = [ ] ;
67+ for ( let i = 0 ; i < requestHeaders . length ; i ++ ) {
68+ if ( requestHeaders [ i ] === SENTRY_BAGGAGE_HEADER ) {
69+ baggagePositions . push ( i ) ;
70+ }
71+ }
72+
73+ if ( baggage && ! baggagePositions . length ) {
6674 requestHeaders . push ( SENTRY_BAGGAGE_HEADER , baggage ) ;
6775 } else if ( baggage ) {
68- const existingBaggage = requestHeaders [ existingBaggagePos + 1 ] ;
69- const merged = mergeBaggageHeaders ( existingBaggage , baggage ) ;
76+ // First, consolidate all existing baggage values into one
77+ let consolidatedBaggage = requestHeaders [ baggagePositions [ 0 ] ! + 1 ] as string ;
78+ for ( let i = baggagePositions . length - 1 ; i >= 1 ; i -- ) {
79+ const pos = baggagePositions [ i ] ! ;
80+ const val = requestHeaders [ pos + 1 ] as string ;
81+ consolidatedBaggage = mergeBaggageHeaders ( consolidatedBaggage , val ) || consolidatedBaggage ;
82+ requestHeaders . splice ( pos , 2 ) ;
83+ }
84+
85+ // Then merge with the new baggage we want to add
86+ const merged = mergeBaggageHeaders ( consolidatedBaggage , baggage ) ;
7087 if ( merged ) {
71- requestHeaders [ existingBaggagePos + 1 ] = merged ;
88+ requestHeaders [ baggagePositions [ 0 ] ! + 1 ] = merged ;
89+ }
90+ }
91+
92+ // Also deduplicate sentry-trace headers — keep only the first occurrence.
93+ // OTel's UndiciInstrumentation may have appended a second one via propagation.inject().
94+ let firstSentryTraceFound = false ;
95+ for ( let i = requestHeaders . length - 2 ; i >= 0 ; i -- ) {
96+ if ( requestHeaders [ i ] === SENTRY_TRACE_HEADER ) {
97+ if ( firstSentryTraceFound ) {
98+ requestHeaders . splice ( i , 2 ) ;
99+ }
100+ firstSentryTraceFound = true ;
72101 }
73102 }
74103 } else {
@@ -82,15 +111,37 @@ export function addTracePropagationHeadersToFetchRequest(
82111 request . headers += `traceparent: ${ traceparent } \r\n` ;
83112 }
84113
85- const existingBaggage = request . headers . match ( BAGGAGE_HEADER_REGEX ) ?. [ 1 ] ;
86- if ( baggage && ! existingBaggage ) {
114+ // Consolidate all duplicate baggage entries into one, then merge with our new baggage.
115+ // OTel's UndiciInstrumentation may append a second baggage header via propagation.inject(),
116+ // so we need to handle multiple entries — not just the first one.
117+ const allBaggageMatches = request . headers . matchAll ( BAGGAGE_HEADER_REGEX_GLOBAL ) ;
118+ let consolidatedBaggage : string | undefined ;
119+ for ( const match of allBaggageMatches ) {
120+ if ( match [ 1 ] ) {
121+ consolidatedBaggage = consolidatedBaggage
122+ ? mergeBaggageHeaders ( consolidatedBaggage , match [ 1 ] ) || consolidatedBaggage
123+ : match [ 1 ] ;
124+ }
125+ }
126+
127+ // Remove all existing baggage entries
128+ request . headers = request . headers . replace ( BAGGAGE_HEADER_REGEX_GLOBAL , '' ) ;
129+
130+ if ( baggage && ! consolidatedBaggage ) {
87131 request . headers += `${ SENTRY_BAGGAGE_HEADER } : ${ baggage } \r\n` ;
88- } else if ( baggage ) {
89- const merged = mergeBaggageHeaders ( existingBaggage , baggage ) ;
132+ } else if ( baggage && consolidatedBaggage ) {
133+ const merged = mergeBaggageHeaders ( consolidatedBaggage , baggage ) ;
90134 if ( merged ) {
91- request . headers = request . headers . replace ( BAGGAGE_HEADER_REGEX , `baggage : ${ merged } \r\n`) ;
135+ request . headers += ` ${ SENTRY_BAGGAGE_HEADER } : ${ merged } \r\n`;
92136 }
93137 }
138+
139+ // Deduplicate sentry-trace headers — keep only the first occurrence.
140+ let sentryTraceCount = 0 ;
141+ request . headers = request . headers . replace ( SENTRY_TRACE_HEADER_REGEX_GLOBAL , match => {
142+ sentryTraceCount ++ ;
143+ return sentryTraceCount === 1 ? match : '' ;
144+ } ) ;
94145 }
95146}
96147
0 commit comments