From 940ffe119280ccf1bc4eb7c5ac0ecd455d49c48f Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Tue, 5 May 2026 14:48:10 -0400 Subject: [PATCH 1/4] fix(replay): Set replay_id on DSC after buffer-to-session conversion When `sendBufferedReplayOrFlush` converts from buffer to session mode, it calls `startRecording()` directly but never updates the cached DSC with the new replay_id. The `createDsc` hook only fires for new spans, not for an already-cached DSC on the scope, so the replay_id was missing from all outgoing requests until a new span was created. Co-Authored-By: Claude Opus 4.6 --- packages/replay-internal/src/replay.ts | 4 ++++ .../src/util/resetReplayIdOnDynamicSamplingContext.ts | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/replay-internal/src/replay.ts b/packages/replay-internal/src/replay.ts index de2e596a0be1..05dcc79b49f6 100644 --- a/packages/replay-internal/src/replay.ts +++ b/packages/replay-internal/src/replay.ts @@ -621,6 +621,10 @@ export class ReplayContainer implements ReplayContainerInterface { } this.startRecording(); + + if (this.session) { + setReplayIdOnDynamicSamplingContext(this.session.id); + } } /** diff --git a/packages/replay-internal/src/util/resetReplayIdOnDynamicSamplingContext.ts b/packages/replay-internal/src/util/resetReplayIdOnDynamicSamplingContext.ts index 4839300d7fd2..9d46cc5fd272 100644 --- a/packages/replay-internal/src/util/resetReplayIdOnDynamicSamplingContext.ts +++ b/packages/replay-internal/src/util/resetReplayIdOnDynamicSamplingContext.ts @@ -1,5 +1,9 @@ -import type { DynamicSamplingContext } from '@sentry/core'; -import { getActiveSpan, getCurrentScope, getDynamicSamplingContextFromSpan } from '@sentry/core'; +import type { DynamicSamplingContext } from "@sentry/core"; +import { + getActiveSpan, + getCurrentScope, + getDynamicSamplingContextFromSpan, +} from "@sentry/core"; /** * Reset the `replay_id` field on the DSC. From 30136efb32489c3445f1a21e5e5fb6f78b75a970 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Tue, 5 May 2026 14:57:43 -0400 Subject: [PATCH 2/4] move into session check block --- packages/replay-internal/src/replay.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/replay-internal/src/replay.ts b/packages/replay-internal/src/replay.ts index 05dcc79b49f6..9bb959b33dd4 100644 --- a/packages/replay-internal/src/replay.ts +++ b/packages/replay-internal/src/replay.ts @@ -618,13 +618,10 @@ export class ReplayContainer implements ReplayContainerInterface { this._updateUserActivity(activityTime); this._updateSessionActivity(activityTime); this._maybeSaveSession(); + setReplayIdOnDynamicSamplingContext(this.session.id); } this.startRecording(); - - if (this.session) { - setReplayIdOnDynamicSamplingContext(this.session.id); - } } /** From 59a516b6a294c2e279377376353fc05c0e04b38b Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Tue, 5 May 2026 15:55:03 -0400 Subject: [PATCH 3/4] test(replay): Add test for replay_id on DSC after buffer-to-session conversion Verifies that a cached DSC on the scope gets replay_id set after sendBufferedReplayOrFlush converts from buffer to session mode. Co-Authored-By: Claude Opus 4.6 --- .../test/integration/errorSampleRate.test.ts | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/replay-internal/test/integration/errorSampleRate.test.ts b/packages/replay-internal/test/integration/errorSampleRate.test.ts index b49882b72034..cf12ff990f00 100644 --- a/packages/replay-internal/test/integration/errorSampleRate.test.ts +++ b/packages/replay-internal/test/integration/errorSampleRate.test.ts @@ -3,7 +3,7 @@ */ import '../utils/mock-internal-setTimeout'; -import { captureException, getClient } from '@sentry/core'; +import { captureException, getClient, getCurrentScope } from '@sentry/core'; import type { MockInstance } from 'vitest'; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { @@ -383,6 +383,28 @@ describe('Integration | errorSampleRate', () => { }); }); + it('sets replay_id on DSC after converting from buffer to session mode', async () => { + const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); + mockRecord._emitter(TEST_EVENT); + + // Simulate a cached DSC on the scope (as browserTracingIntegration would set) + getCurrentScope().setPropagationContext({ + traceId: '00000000000000000000000000000000', + sampleRand: 0, + dsc: { trace_id: '00000000000000000000000000000000', sampled: 'true' }, + }); + + expect(replay.recordingMode).toBe('buffer'); + const dsc = getCurrentScope().getPropagationContext().dsc!; + expect(dsc.replay_id).toBeUndefined(); + + await replay.sendBufferedReplayOrFlush({ continueRecording: true }); + await vi.advanceTimersToNextTimerAsync(); + + expect(replay.recordingMode).toBe('session'); + expect(dsc.replay_id).toBe(replay.getSessionId()); + }); + // This tests a regression where we were calling flush indiscriminantly in `stop()` it('does not upload a replay event if error is not sampled', async () => { // We are trying to replicate the case where error rate is 0 and session From 4180690fd0cfa1e9cb5e3f3749ef17ae442dadd8 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Wed, 13 May 2026 17:11:31 -0400 Subject: [PATCH 4/4] lint --- .../src/util/resetReplayIdOnDynamicSamplingContext.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/replay-internal/src/util/resetReplayIdOnDynamicSamplingContext.ts b/packages/replay-internal/src/util/resetReplayIdOnDynamicSamplingContext.ts index 9d46cc5fd272..4839300d7fd2 100644 --- a/packages/replay-internal/src/util/resetReplayIdOnDynamicSamplingContext.ts +++ b/packages/replay-internal/src/util/resetReplayIdOnDynamicSamplingContext.ts @@ -1,9 +1,5 @@ -import type { DynamicSamplingContext } from "@sentry/core"; -import { - getActiveSpan, - getCurrentScope, - getDynamicSamplingContextFromSpan, -} from "@sentry/core"; +import type { DynamicSamplingContext } from '@sentry/core'; +import { getActiveSpan, getCurrentScope, getDynamicSamplingContextFromSpan } from '@sentry/core'; /** * Reset the `replay_id` field on the DSC.