Skip to content

Commit f5ee1e4

Browse files
authored
fix(v10/core): Avoid propagating baggage: "undefined" when DSC is missing (#22440)
Backport of #22424
1 parent 86d017f commit f5ee1e4

6 files changed

Lines changed: 91 additions & 3 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
integrations: [Sentry.browserTracingIntegration()],
8+
tracePropagationTargets: ['sentry-test-external.io'],
9+
tracesSampleRate: 1,
10+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Sentry.startSpan({ name: 'standalone_span', experimental: { standalone: true } }, () => {
2+
fetch('http://sentry-test-external.io');
3+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="sentry-trace" content="12345678901234567890123456789012-1234567890123456-1" />
6+
<!-- baggage meta tag omitted on purpose -->
7+
</head>
8+
</html>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { expect } from '@playwright/test';
2+
import type { SpanEnvelope } from '@sentry/core';
3+
import { sentryTest } from '../../../../utils/fixtures';
4+
import {
5+
getMultipleSentryEnvelopeRequests,
6+
properFullEnvelopeRequestParser,
7+
shouldSkipTracingTest,
8+
} from '../../../../utils/helpers';
9+
10+
const TRACE_ID = '12345678901234567890123456789012';
11+
const OUTGOING_REQUEST_URL = 'http://sentry-test-external.io';
12+
13+
sentryTest(
14+
'omits the trace envelope header when a standalone span continues a trace without baggage',
15+
async ({ getLocalTestUrl, page }) => {
16+
sentryTest.skip(shouldSkipTracingTest());
17+
18+
const url = await getLocalTestUrl({ testDir: __dirname });
19+
await page.route(OUTGOING_REQUEST_URL, route => route.fulfill({ status: 200, body: 'ok' }));
20+
const outgoingRequestPromise = page.waitForRequest(OUTGOING_REQUEST_URL);
21+
22+
const [spanEnvelope] = await getMultipleSentryEnvelopeRequests<SpanEnvelope>(
23+
page,
24+
1,
25+
{ url, envelopeType: 'span' },
26+
properFullEnvelopeRequestParser,
27+
);
28+
const outgoingRequest = await outgoingRequestPromise;
29+
30+
expect(spanEnvelope[0]).toEqual({
31+
sent_at: expect.any(String),
32+
});
33+
34+
// To be clear: This is _expected_ behavior, not a bug.
35+
// SDKs must assume that an incoming `sentry-trace` but no `baggage` meta tag means that the
36+
// trace was started from an SDK that's not yet compatible with the DSC or baggage propagation.
37+
// The test demonstrates that the SDK as expected continues the trace but does not send a `trace`
38+
// header, nor a baggage header.
39+
expect(spanEnvelope[0].trace).toBeUndefined();
40+
41+
expect(spanEnvelope[1]).toHaveLength(1);
42+
expect(spanEnvelope[1][0][1].trace_id).toBe(TRACE_ID);
43+
44+
const outgoingRequestHeaders = outgoingRequest.headers();
45+
expect(outgoingRequestHeaders['sentry-trace']).toMatch(new RegExp(`^${TRACE_ID}-[\\da-f]{16}-1$`));
46+
expect(outgoingRequestHeaders['baggage']).toBeUndefined();
47+
},
48+
);

packages/core/src/fetch.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,11 @@ export function _INTERNAL_getTracingHeadersForFetchRequest(
223223
const originalHeaders = fetchOptionsObj.headers || (isRequest(request) ? request.headers : undefined);
224224

225225
if (!originalHeaders) {
226-
return { ...traceHeaders };
226+
return {
227+
'sentry-trace': sentryTrace,
228+
...(baggage && { baggage }),
229+
...(traceparent && { traceparent }),
230+
};
227231
} else if (isHeaders(originalHeaders)) {
228232
const newHeaders = new Headers(originalHeaders);
229233

@@ -293,11 +297,11 @@ export function _INTERNAL_getTracingHeadersForFetchRequest(
293297

294298
const newHeaders: {
295299
'sentry-trace': string;
296-
baggage: string | undefined;
300+
baggage?: string;
297301
traceparent?: string;
298302
} = Object.assign({}, originalHeaders, {
299303
'sentry-trace': (existingSentryTraceHeader as string | undefined) ?? sentryTrace,
300-
baggage: newBaggageHeaders.length > 0 ? newBaggageHeaders.join(',') : undefined,
304+
...(newBaggageHeaders.length > 0 && { baggage: newBaggageHeaders.join(',') }),
301305
});
302306

303307
if (propagateTraceparent && traceparent && !existingTraceparentHeader) {

packages/core/test/lib/fetch.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,21 @@ describe('_INTERNAL_getTracingHeadersForFetchRequest', () => {
7070
});
7171
});
7272

73+
it('omits baggage from headers object when no baggage is available', () => {
74+
vi.mocked(traceData.getTraceData).mockReturnValueOnce({
75+
'sentry-trace': DEFAULT_SENTRY_TRACE,
76+
});
77+
78+
const returnedHeaders = _INTERNAL_getTracingHeadersForFetchRequest('/api/test', {
79+
headers: { 'custom-header': 'custom-value' },
80+
});
81+
82+
expect(returnedHeaders).toStrictEqual({
83+
'sentry-trace': DEFAULT_SENTRY_TRACE,
84+
'custom-header': 'custom-value',
85+
});
86+
});
87+
7388
it('attaches sentry headers to a Headers instance', () => {
7489
const returnedHeaders = _INTERNAL_getTracingHeadersForFetchRequest('/api/test', {
7590
headers: new Headers({ 'custom-header': 'custom-value' }),

0 commit comments

Comments
 (0)