From 7cce069a8d3d06ddb9e597e67848b8cc2efc9de1 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 7 May 2026 15:15:01 +0200 Subject: [PATCH 1/2] fix(core): Keep spanless baggage mutable Only freeze scope baggage during spanless propagation when it carries incoming Sentry DSC values that should be protected. This keeps local propagation baggage writable for a later transaction so transaction-specific DSC can be emitted. Co-Authored-By: Claude --- .../java/io/sentry/util/TracingUtils.java | 4 +- .../java/io/sentry/util/TracingUtilsTest.kt | 43 ++++++++++++++++--- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/sentry/src/main/java/io/sentry/util/TracingUtils.java b/sentry/src/main/java/io/sentry/util/TracingUtils.java index 9de7adec4b..8b981276f4 100644 --- a/sentry/src/main/java/io/sentry/util/TracingUtils.java +++ b/sentry/src/main/java/io/sentry/util/TracingUtils.java @@ -115,7 +115,9 @@ public static void setTrace( @NotNull Baggage baggage = propagationContext.getBaggage(); if (baggage.isMutable()) { baggage.setValuesFromScope(scope, sentryOptions); - baggage.freeze(); + if (baggage.isShouldFreeze()) { + baggage.freeze(); + } } }); } diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index edfcc361b0..b4767b9da1 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -16,6 +16,7 @@ import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext import io.sentry.W3CTraceparentHeader import io.sentry.protocol.SentryId +import io.sentry.protocol.TransactionNameSource import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotEquals @@ -77,7 +78,7 @@ class TracingUtilsTest { .value .contains("sentry-trace_id=${fixture.scope.propagationContext.traceId}") ) - assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) + assertTrue(fixture.scope.propagationContext.baggage!!.isMutable) } @Test @@ -98,7 +99,7 @@ class TracingUtilsTest { assertEquals(fixture.scope.propagationContext.traceId, headers.sentryTraceHeader.traceId) assertEquals(fixture.scope.propagationContext.isSampled, headers.sentryTraceHeader.isSampled) assertTrue(headers.baggageHeader!!.value.contains("some-baggage-key=some-baggage-value")) - assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) + assertTrue(fixture.scope.propagationContext.baggage!!.isMutable) } @Test @@ -120,7 +121,7 @@ class TracingUtilsTest { assertEquals(fixture.scope.propagationContext.traceId, headers.sentryTraceHeader.traceId) assertEquals(fixture.scope.propagationContext.isSampled, headers.sentryTraceHeader.isSampled) assertTrue(headers.baggageHeader!!.value.contains("some-baggage-key=some-baggage-value")) - assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) + assertTrue(fixture.scope.propagationContext.baggage!!.isMutable) } @Test @@ -142,7 +143,7 @@ class TracingUtilsTest { assertEquals(fixture.scope.propagationContext.traceId, headers.sentryTraceHeader.traceId) assertEquals(fixture.scope.propagationContext.isSampled, headers.sentryTraceHeader.isSampled) assertTrue(headers.baggageHeader!!.value.contains("some-baggage-key=some-baggage-value")) - assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) + assertTrue(fixture.scope.propagationContext.baggage!!.isMutable) } @Test @@ -164,7 +165,7 @@ class TracingUtilsTest { assertEquals(fixture.scope.propagationContext.traceId, headers.sentryTraceHeader.traceId) assertEquals(fixture.scope.propagationContext.isSampled, headers.sentryTraceHeader.isSampled) assertTrue(headers.baggageHeader!!.value.contains("some-baggage-key=some-baggage-value")) - assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) + assertTrue(fixture.scope.propagationContext.baggage!!.isMutable) } @Test @@ -301,7 +302,7 @@ class TracingUtilsTest { } @Test - fun `updates mutable baggage`() { + fun `updates mutable baggage without freezing local baggage`() { fixture.setup() // not frozen because it doesn't contain sentry-* keys fixture.scope.propagationContext = @@ -319,7 +320,35 @@ class TracingUtilsTest { fixture.scope.propagationContext.traceId.toString(), fixture.scope.propagationContext.baggage!!.traceId, ) - assertFalse(fixture.scope.propagationContext.baggage!!.isMutable) + assertTrue(fixture.scope.propagationContext.baggage!!.isMutable) + } + + @Test + fun `spanless propagation leaves baggage mutable for later transaction DSC`() { + fixture.setup() + val propagationContext = fixture.scope.propagationContext + + TracingUtils.maybeUpdateBaggage(fixture.scope, fixture.options) + + assertTrue(propagationContext.baggage.isMutable) + assertNull(propagationContext.baggage.transaction) + assertNull(propagationContext.baggage.sampleRate) + assertNull(propagationContext.baggage.sampled) + + val transactionContext = TransactionContext.fromPropagationContext(propagationContext) + transactionContext.name = "GET /session" + transactionContext.transactionNameSource = TransactionNameSource.CUSTOM + transactionContext.setSamplingDecision( + TracesSamplingDecision(true, 0.5, propagationContext.sampleRand) + ) + val tracer = SentryTracer(transactionContext, fixture.scopes) + + val baggageHeader = tracer.toBaggageHeader(null) + + assertNotNull(baggageHeader) + assertTrue(baggageHeader.value.contains("sentry-transaction=GET%20%2Fsession")) + assertTrue(baggageHeader.value.contains("sentry-sampled=true")) + assertTrue(baggageHeader.value.contains("sentry-sample_rate=0.5")) } @Test From 1682a443f6353ca639ca0eda3b93e0e2617d6510 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 7 May 2026 15:21:49 +0200 Subject: [PATCH 2/2] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 681753db08..83a8b94fba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ ### Fixes +- Fix transaction-specific dynamic sampling context fields missing after earlier spanless outgoing requests ([#5384](https://github.com/getsentry/sentry-java/pull/5384)) - Fix soft input keyboard not being shown on the Feedback form ([#5359](https://github.com/getsentry/sentry-java/pull/5359)) - Fix shake-to-report not triggering on some devices due to high acceleration threshold ([#5366](https://github.com/getsentry/sentry-java/pull/5366)) - Fix feedback form retaining previous message when shown again via shake ([#5366](https://github.com/getsentry/sentry-java/pull/5366))