From 87d27ebf950cf3701a0e68762b6ea40d5d47f63e Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 2 Jun 2026 10:46:37 +0200 Subject: [PATCH 1/2] fix(browser): Correctly parse sampleRate when `consistentTraceSampling` is enabled --- packages/browser/src/tracing/linkedTraces.ts | 5 +- .../browser/test/tracing/linkedTraces.test.ts | 89 ++++++++++++++++++- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/packages/browser/src/tracing/linkedTraces.ts b/packages/browser/src/tracing/linkedTraces.ts index 8b13ff99fb35..88bdaa744671 100644 --- a/packages/browser/src/tracing/linkedTraces.ts +++ b/packages/browser/src/tracing/linkedTraces.ts @@ -141,9 +141,10 @@ export function addPreviousTraceSpanLink( function getSampleRate(): number { try { - return ( - Number(oldPropagationContext.dsc?.sample_rate) ?? Number(spanJson.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]) + const oldSampleRate = Number( + spanJson.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE] ?? oldPropagationContext.dsc?.sample_rate, ); + return Number.isNaN(oldSampleRate) ? 0 : oldSampleRate; } catch { return 0; } diff --git a/packages/browser/test/tracing/linkedTraces.test.ts b/packages/browser/test/tracing/linkedTraces.test.ts index 9426a67dec08..64f917c03ff6 100644 --- a/packages/browser/test/tracing/linkedTraces.test.ts +++ b/packages/browser/test/tracing/linkedTraces.test.ts @@ -1,5 +1,12 @@ -import type { Span } from '@sentry/core/browser'; -import { addChildSpanToSpan, debug, SentrySpan, spanToJSON, timestampInSeconds } from '@sentry/core/browser'; +import type { PropagationContext, Span } from '@sentry/core/browser'; +import { + addChildSpanToSpan, + debug, + SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SentrySpan, + spanToJSON, + timestampInSeconds, +} from '@sentry/core/browser'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { BrowserClient } from '../../src'; import type { PreviousTraceInfo } from '../../src/tracing/linkedTraces'; @@ -201,6 +208,84 @@ describe('addPreviousTraceSpanLink', () => { }); }); + it.each([NaN, undefined])( + 'falls back to sampleRate 0 if the sample rate in the previous trace info is %s', + sampleRate => { + const currentSpanStart = timestampInSeconds(); + + const previousTraceInfo: PreviousTraceInfo = { + spanContext: { traceId: '123', spanId: '456', traceFlags: 1 }, + startTimestamp: currentSpanStart - PREVIOUS_TRACE_MAX_DURATION + 1, + sampleRand: 0.0126, + sampleRate: sampleRate as number, + }; + + const currentSpan = new SentrySpan({ + name: 'test', + startTimestamp: currentSpanStart, + parentSpanId: '789', + spanId: 'abc', + traceId: 'def', + sampled: true, + }); + + const oldPropagationContext: PropagationContext = { + sampleRand: 0.0126, + traceId: '123', + sampled: true, + dsc: { sample_rand: '0.0126', sample_rate: String(sampleRate) }, + }; + + const updatedPreviousTraceInfo = addPreviousTraceSpanLink(previousTraceInfo, currentSpan, oldPropagationContext); + + expect(updatedPreviousTraceInfo).toEqual({ + spanContext: currentSpan.spanContext(), + startTimestamp: currentSpanStart, + sampleRand: 0.0126, + sampleRate: 0, + }); + }, + ); + + it.each([NaN, undefined])('falls back to sampleRate 0 if the sample rate on the spa or DSC is %s', sampleRate => { + const currentSpanStart = timestampInSeconds(); + + const previousTraceInfo: PreviousTraceInfo = { + spanContext: { traceId: '123', spanId: '456', traceFlags: 1 }, + startTimestamp: currentSpanStart - PREVIOUS_TRACE_MAX_DURATION + 1, + sampleRand: 0.0126, + sampleRate: 0, + }; + + const currentSpan = new SentrySpan({ + name: 'test', + startTimestamp: currentSpanStart, + parentSpanId: '789', + spanId: 'abc', + traceId: 'def', + sampled: true, + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: sampleRate, + }, + }); + + const oldPropagationContext: PropagationContext = { + sampleRand: 0.0126, + traceId: '123', + sampled: true, + dsc: { sample_rand: '0.0126', sample_rate: String(sampleRate) }, + }; + + const updatedPreviousTraceInfo = addPreviousTraceSpanLink(previousTraceInfo, currentSpan, oldPropagationContext); + + expect(updatedPreviousTraceInfo).toEqual({ + spanContext: currentSpan.spanContext(), + startTimestamp: currentSpanStart, + sampleRand: 0.0126, + sampleRate: 0, + }); + }); + it('logs a debug message when adding a previous trace link (with stringified context)', () => { const debugLogSpy = vi.spyOn(debug, 'log'); From da03ab33deae2a1e3e0e5df44dc205b57276e50a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 2 Jun 2026 11:58:17 +0200 Subject: [PATCH 2/2] add another unit test --- .../browser/test/tracing/linkedTraces.test.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/browser/test/tracing/linkedTraces.test.ts b/packages/browser/test/tracing/linkedTraces.test.ts index 64f917c03ff6..cb01df0c539b 100644 --- a/packages/browser/test/tracing/linkedTraces.test.ts +++ b/packages/browser/test/tracing/linkedTraces.test.ts @@ -286,6 +286,45 @@ describe('addPreviousTraceSpanLink', () => { }); }); + it('prefers sample rate from span over sample rate from DSC', () => { + const currentSpanStart = timestampInSeconds(); + + const previousTraceInfo: PreviousTraceInfo = { + spanContext: { traceId: '123', spanId: '456', traceFlags: 1 }, + startTimestamp: currentSpanStart - PREVIOUS_TRACE_MAX_DURATION + 1, + sampleRand: 0.0126, + sampleRate: 0.4, + }; + + const currentSpan = new SentrySpan({ + name: 'test', + startTimestamp: currentSpanStart, + parentSpanId: '789', + spanId: 'abc', + traceId: 'def', + sampled: true, + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 0.5, + }, + }); + + const oldPropagationContext: PropagationContext = { + sampleRand: 0.0126, + traceId: '123', + sampled: true, + dsc: { sample_rand: '0.0126', sample_rate: '0.6' }, + }; + + const updatedPreviousTraceInfo = addPreviousTraceSpanLink(previousTraceInfo, currentSpan, oldPropagationContext); + + expect(updatedPreviousTraceInfo).toEqual({ + spanContext: currentSpan.spanContext(), + startTimestamp: currentSpanStart, + sampleRand: 0.0126, + sampleRate: 0.5, + }); + }); + it('logs a debug message when adding a previous trace link (with stringified context)', () => { const debugLogSpy = vi.spyOn(debug, 'log');