From 5e3545712b2d3ade96ad88e5e75c8580150a10ce Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 31 Jul 2026 12:06:37 +0200 Subject: [PATCH 1/8] feat(core): Deprecate `beforeSendTransaction` and `ignoreTransactions` Both options operate on the transaction event that span streaming no longer produces, so they silently stopped taking effect when span streaming became the default. That is easy to miss when upgrading. Deprecate both options with a note that they only apply in combination with `traceLifecycle: 'static'` and will be removed in v12, and warn at init time when either is set while span streaming is enabled. The warning writes to the console directly rather than through `debug`, which is opt-in and stripped from non-debug bundles, so users who need to see it actually do. The check runs after integration setup because `spanStreamingIntegration` can fall back to the static trace lifecycle, in which case both options do apply and we must stay silent. Fixes #22856 Co-Authored-By: Claude Opus 5 (1M context) --- MIGRATION.md | 23 ++++-- .../src/core/sentry/telemetry.ts | 2 + packages/core/src/client.ts | 5 ++ packages/core/src/types/options.ts | 12 +++ .../warnAboutIgnoredTransactionOptions.ts | 38 ++++++++++ packages/core/test/lib/client.test.ts | 40 ++++++++++ ...warnAboutIgnoredTransactionOptions.test.ts | 74 +++++++++++++++++++ 7 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts create mode 100644 packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts diff --git a/MIGRATION.md b/MIGRATION.md index ff01d0d8a266..9fe23411fd4c 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -122,13 +122,24 @@ Affected SDKs: All SDKs. Each span is sent to Sentry the moment it finishes instead of being buffered until the root span completes. This means spans are no longer bound by the 1000-span per transaction limit and their individual payload-size limits have been increased. -The new model comes with some changes to Sentry hooks such as `beforeSendSpan` or options like `ignoreSpans` and requires manual migration. `beforeSendTransaction` and `ignoreTransactions` will **no-op**. Users who cannot migrate yet can opt into the previous transaction-based static model. +The new model comes with some changes to Sentry hooks such as `beforeSendSpan` or options like `ignoreSpans` and requires manual migration. -> **TODO(v11):** The migration path for span streaming is still being defined. Document: -> -> - the concrete before/after for `beforeSendSpan` and `ignoreSpans`, -> - the exact replacement for `beforeSendTransaction` / `ignoreTransactions`, -> - how to opt back into the transaction-based model (option name + example). +`beforeSendTransaction` and `ignoreTransactions` are **deprecated** and will be removed in v12. Both operate on the +transaction event that span streaming no longer produces, so the SDK **ignores** them and logs a console warning if it +finds either option in your `Sentry.init()` call. Migrate to `beforeSendSpan` and `ignoreSpans`, which apply to both +trace lifecycles. + +If you cannot migrate yet, opt back into the transaction-based model, which keeps both options working until they are +removed: + +```js +Sentry.init({ + traceLifecycle: 'static', +}); +``` + +> **TODO(v11):** The migration path for span streaming is still being defined. Document the concrete before/after for +> `beforeSendSpan` and `ignoreSpans`. ### Logs are enabled by default diff --git a/packages/bundler-plugins/src/core/sentry/telemetry.ts b/packages/bundler-plugins/src/core/sentry/telemetry.ts index 5615ac3e8f6f..6e26d5287e6d 100644 --- a/packages/bundler-plugins/src/core/sentry/telemetry.ts +++ b/packages/bundler-plugins/src/core/sentry/telemetry.ts @@ -45,6 +45,8 @@ export function createSentryInstance( return event; }, + // Deprecated, but still applied because this client runs on the static trace lifecycle. + // eslint-disable-next-line typescript/no-deprecated beforeSendTransaction: event => { delete event.server_name; // Server name might contain PII return event; diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 6076b065cf62..b0a4c485b2bf 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -54,6 +54,7 @@ import { reparentChildSpans, shouldIgnoreSpan } from './utils/should-ignore-span import { showSpanDropWarning } from './utils/spanUtils'; import { safeUnref } from './utils/timer'; import { convertSpanJsonToTransactionEvent, convertTransactionEventToSpanJson } from './utils/transactionEvent'; +import { warnAboutIgnoredTransactionOptions } from './utils/warnAboutIgnoredTransactionOptions'; import { resolveDataCollectionOptions } from './utils/data-collection/resolveDataCollectionOptions'; const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured."; @@ -508,6 +509,9 @@ export abstract class Client { ) { this._setupIntegrations(); } + + // Checked after integration setup because `spanStreamingIntegration` may fall back to the static trace lifecycle. + warnAboutIgnoredTransactionOptions(this._options); } /** @@ -1653,6 +1657,7 @@ function processBeforeSend( event: Event, hint: EventHint, ): PromiseLike | Event | null { + // eslint-disable-next-line typescript/no-deprecated const { beforeSend, beforeSendTransaction, ignoreSpans } = options; const beforeSendSpan = !isStreamedBeforeSendSpanCallback(options.beforeSendSpan) && options.beforeSendSpan; diff --git a/packages/core/src/types/options.ts b/packages/core/src/types/options.ts index e5f938082509..0a5de5b716c1 100644 --- a/packages/core/src/types/options.ts +++ b/packages/core/src/types/options.ts @@ -347,6 +347,12 @@ export interface ClientOptions; @@ -649,6 +655,12 @@ export interface ClientOptions { + // eslint-disable-next-line no-console + console.warn( + `[Sentry] Your \`Sentry.init()\` options include ${ignoredOptions.map(option => `\`${option}\``).join(' and ')}, which the SDK ignores because span streaming is enabled. ` + + 'Use `beforeSendSpan` and `ignoreSpans` instead. ' + + "Alternatively, set `traceLifecycle: 'static'` to keep the transaction-based options working until they are removed in v12.", + ); + }); +} diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index 5b0cb752f2c2..97758ae6d631 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -96,6 +96,46 @@ describe('Client', () => { }); }); + describe('init() / transaction option warnings', () => { + test('warns about transaction options ignored by span streaming', () => { + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + + const options = getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + traceLifecycle: 'stream', + beforeSendTransaction: event => event, + ignoreTransactions: ['/healthcheck'], + }); + new TestClient(options).init(); + + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`beforeSendTransaction` and `ignoreTransactions`'); + consoleWarnSpy.mockRestore(); + }); + + test('stays silent when an integration falls back to the static trace lifecycle during setup', () => { + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + + const options = getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + traceLifecycle: 'stream', + beforeSendTransaction: event => event, + integrations: [ + { + name: 'FallsBackToStatic', + setup: client => { + client.getOptions().traceLifecycle = 'static'; + }, + }, + ], + }); + new TestClient(options).init(); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + consoleWarnSpy.mockRestore(); + }); + }); + describe('getOptions()', () => { test('returns the options', () => { expect.assertions(1); diff --git a/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts b/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts new file mode 100644 index 000000000000..55f478cde178 --- /dev/null +++ b/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts @@ -0,0 +1,74 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import type { ClientOptions } from '../../../src/types/options'; +import * as debugLoggerModule from '../../../src/utils/debug-logger'; +import { warnAboutIgnoredTransactionOptions } from '../../../src/utils/warnAboutIgnoredTransactionOptions'; + +describe('warnAboutIgnoredTransactionOptions', () => { + let consoleWarnSpy: ReturnType; + + beforeEach(() => { + consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + vi.spyOn(debugLoggerModule, 'consoleSandbox').mockImplementation(cb => cb()); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + function options(overrides: Partial): ClientOptions { + return { integrations: [], transport: () => ({}) as never, stackParser: () => [], ...overrides } as ClientOptions; + } + + it('warns about `beforeSendTransaction` when span streaming is enabled', () => { + warnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream', beforeSendTransaction: event => event })); + + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`beforeSendTransaction`'); + expect(consoleWarnSpy.mock.calls[0]?.[0]).not.toContain('`ignoreTransactions`'); + }); + + it('warns about `ignoreTransactions` when span streaming is enabled', () => { + warnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream', ignoreTransactions: ['/healthcheck'] })); + + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`ignoreTransactions`'); + expect(consoleWarnSpy.mock.calls[0]?.[0]).not.toContain('`beforeSendTransaction`'); + }); + + it('warns once about both options when both are set', () => { + warnAboutIgnoredTransactionOptions( + options({ + traceLifecycle: 'stream', + beforeSendTransaction: event => event, + ignoreTransactions: ['/healthcheck'], + }), + ); + + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`beforeSendTransaction` and `ignoreTransactions`'); + }); + + it('does not warn when the trace lifecycle is static', () => { + warnAboutIgnoredTransactionOptions( + options({ + traceLifecycle: 'static', + beforeSendTransaction: event => event, + ignoreTransactions: ['/healthcheck'], + }), + ); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + + it('does not warn when neither option is set', () => { + warnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream' })); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + + it('does not warn for an empty `ignoreTransactions` array', () => { + warnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream', ignoreTransactions: [] })); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); +}); From 98c58ac417d6a3f912eb1745f13e561af29b9b29 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 31 Jul 2026 13:11:26 +0200 Subject: [PATCH 2/8] cleanup --- AGENTS.md | 2 ++ MIGRATION.md | 23 +++++-------------- packages/core/src/client.ts | 5 ++-- packages/core/src/types/options.ts | 17 +++++++++----- .../warnAboutIgnoredTransactionOptions.ts | 14 +++++------ packages/core/test/lib/client.test.ts | 6 ++++- ...warnAboutIgnoredTransactionOptions.test.ts | 20 +++++++++------- 7 files changed, 45 insertions(+), 42 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 62374358ad61..7ca4e8b264f8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -134,6 +134,8 @@ Uses **Git Flow** (see `docs/gitflow.md`). - Never expose secrets or keys - When modifying files, cover all occurrences (including `src/` and `test/`) - Comments explain **why**, never **what** — never add a comment that restates what the code does or describes the change being made; only comment when the reasoning isn't obvious from the code itself +- Do not use `expect(someSpy.mock.calls[0]?.[0])` or similar constructs to check what a spy was called with. + Instead use `expect(someSpy).toHaveBeenCalledWith(...)` or derivatives for a more readable and less brittle test assertion. ## Lazy Loading Is a Last Resort diff --git a/MIGRATION.md b/MIGRATION.md index 9fe23411fd4c..ff01d0d8a266 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -122,24 +122,13 @@ Affected SDKs: All SDKs. Each span is sent to Sentry the moment it finishes instead of being buffered until the root span completes. This means spans are no longer bound by the 1000-span per transaction limit and their individual payload-size limits have been increased. -The new model comes with some changes to Sentry hooks such as `beforeSendSpan` or options like `ignoreSpans` and requires manual migration. +The new model comes with some changes to Sentry hooks such as `beforeSendSpan` or options like `ignoreSpans` and requires manual migration. `beforeSendTransaction` and `ignoreTransactions` will **no-op**. Users who cannot migrate yet can opt into the previous transaction-based static model. -`beforeSendTransaction` and `ignoreTransactions` are **deprecated** and will be removed in v12. Both operate on the -transaction event that span streaming no longer produces, so the SDK **ignores** them and logs a console warning if it -finds either option in your `Sentry.init()` call. Migrate to `beforeSendSpan` and `ignoreSpans`, which apply to both -trace lifecycles. - -If you cannot migrate yet, opt back into the transaction-based model, which keeps both options working until they are -removed: - -```js -Sentry.init({ - traceLifecycle: 'static', -}); -``` - -> **TODO(v11):** The migration path for span streaming is still being defined. Document the concrete before/after for -> `beforeSendSpan` and `ignoreSpans`. +> **TODO(v11):** The migration path for span streaming is still being defined. Document: +> +> - the concrete before/after for `beforeSendSpan` and `ignoreSpans`, +> - the exact replacement for `beforeSendTransaction` / `ignoreTransactions`, +> - how to opt back into the transaction-based model (option name + example). ### Logs are enabled by default diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index b0a4c485b2bf..1e342941305e 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -54,7 +54,7 @@ import { reparentChildSpans, shouldIgnoreSpan } from './utils/should-ignore-span import { showSpanDropWarning } from './utils/spanUtils'; import { safeUnref } from './utils/timer'; import { convertSpanJsonToTransactionEvent, convertTransactionEventToSpanJson } from './utils/transactionEvent'; -import { warnAboutIgnoredTransactionOptions } from './utils/warnAboutIgnoredTransactionOptions'; +import { maybeWarnAboutIgnoredTransactionOptions } from './utils/warnAboutIgnoredTransactionOptions'; import { resolveDataCollectionOptions } from './utils/data-collection/resolveDataCollectionOptions'; const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured."; @@ -510,8 +510,7 @@ export abstract class Client { this._setupIntegrations(); } - // Checked after integration setup because `spanStreamingIntegration` may fall back to the static trace lifecycle. - warnAboutIgnoredTransactionOptions(this._options); + maybeWarnAboutIgnoredTransactionOptions(this._options); } /** diff --git a/packages/core/src/types/options.ts b/packages/core/src/types/options.ts index 0a5de5b716c1..b7730b720c47 100644 --- a/packages/core/src/types/options.ts +++ b/packages/core/src/types/options.ts @@ -343,13 +343,16 @@ export interface ClientOptions { - // eslint-disable-next-line no-console + // oxlint-disable-next-line no-console console.warn( - `[Sentry] Your \`Sentry.init()\` options include ${ignoredOptions.map(option => `\`${option}\``).join(' and ')}, which the SDK ignores because span streaming is enabled. ` + - 'Use `beforeSendSpan` and `ignoreSpans` instead. ' + - "Alternatively, set `traceLifecycle: 'static'` to keep the transaction-based options working until they are removed in v12.", + `[Sentry] Your \`Sentry.init()\` options include ${ignoredOptions.map(option => `\`${option}\``).join(' and ')}, which are currently ignored by the SDK. +Use \`beforeSendSpan\` and \`ignoreSpans\` instead. +Alternatively, set \`traceLifecycle: 'static'\` to opt out of streaming spans and keep the transaction-based options working.`, ); }); } diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index 97758ae6d631..cb8d82663a02 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -109,7 +109,11 @@ describe('Client', () => { new TestClient(options).init(); expect(consoleWarnSpy).toHaveBeenCalledTimes(1); - expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`beforeSendTransaction` and `ignoreTransactions`'); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining( + '`beforeSendTransaction` and `ignoreTransactions`, which are currently ignored by the SDK.', + ), + ); consoleWarnSpy.mockRestore(); }); diff --git a/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts b/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts index 55f478cde178..6e5feae64786 100644 --- a/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts +++ b/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts @@ -1,9 +1,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import type { ClientOptions } from '../../../src/types/options'; import * as debugLoggerModule from '../../../src/utils/debug-logger'; -import { warnAboutIgnoredTransactionOptions } from '../../../src/utils/warnAboutIgnoredTransactionOptions'; +import { maybeWarnAboutIgnoredTransactionOptions } from '../../../src/utils/warnAboutIgnoredTransactionOptions'; -describe('warnAboutIgnoredTransactionOptions', () => { +describe('maybeWarnAboutIgnoredTransactionOptions', () => { let consoleWarnSpy: ReturnType; beforeEach(() => { @@ -20,7 +20,9 @@ describe('warnAboutIgnoredTransactionOptions', () => { } it('warns about `beforeSendTransaction` when span streaming is enabled', () => { - warnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream', beforeSendTransaction: event => event })); + maybeWarnAboutIgnoredTransactionOptions( + options({ traceLifecycle: 'stream', beforeSendTransaction: event => event }), + ); expect(consoleWarnSpy).toHaveBeenCalledTimes(1); expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`beforeSendTransaction`'); @@ -28,7 +30,9 @@ describe('warnAboutIgnoredTransactionOptions', () => { }); it('warns about `ignoreTransactions` when span streaming is enabled', () => { - warnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream', ignoreTransactions: ['/healthcheck'] })); + maybeWarnAboutIgnoredTransactionOptions( + options({ traceLifecycle: 'stream', ignoreTransactions: ['/healthcheck'] }), + ); expect(consoleWarnSpy).toHaveBeenCalledTimes(1); expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`ignoreTransactions`'); @@ -36,7 +40,7 @@ describe('warnAboutIgnoredTransactionOptions', () => { }); it('warns once about both options when both are set', () => { - warnAboutIgnoredTransactionOptions( + maybeWarnAboutIgnoredTransactionOptions( options({ traceLifecycle: 'stream', beforeSendTransaction: event => event, @@ -49,7 +53,7 @@ describe('warnAboutIgnoredTransactionOptions', () => { }); it('does not warn when the trace lifecycle is static', () => { - warnAboutIgnoredTransactionOptions( + maybeWarnAboutIgnoredTransactionOptions( options({ traceLifecycle: 'static', beforeSendTransaction: event => event, @@ -61,13 +65,13 @@ describe('warnAboutIgnoredTransactionOptions', () => { }); it('does not warn when neither option is set', () => { - warnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream' })); + maybeWarnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream' })); expect(consoleWarnSpy).not.toHaveBeenCalled(); }); it('does not warn for an empty `ignoreTransactions` array', () => { - warnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream', ignoreTransactions: [] })); + maybeWarnAboutIgnoredTransactionOptions(options({ traceLifecycle: 'stream', ignoreTransactions: [] })); expect(consoleWarnSpy).not.toHaveBeenCalled(); }); From 2415c4bafff1d285892ca7bb4f288b689d14eb0c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 31 Jul 2026 13:13:22 +0200 Subject: [PATCH 3/8] streamline warning --- .../core/src/utils/warnAboutIgnoredTransactionOptions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts index ca8b10b32aeb..0af1ce0ecc76 100644 --- a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts +++ b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts @@ -18,9 +18,9 @@ export function maybeWarnAboutIgnoredTransactionOptions(options: ClientOptions): const ignoredOptions = [ // oxlint-disable-next-line typescript/no-deprecated - options.beforeSendTransaction && 'beforeSendTransaction', + options.beforeSendTransaction && '\`beforeSendTransaction\`', // oxlint-disable-next-line typescript/no-deprecated - options.ignoreTransactions?.length && 'ignoreTransactions', + options.ignoreTransactions?.length && '\`ignoreTransactions\`', ].filter(Boolean); if (!ignoredOptions.length) { @@ -30,7 +30,7 @@ export function maybeWarnAboutIgnoredTransactionOptions(options: ClientOptions): consoleSandbox(() => { // oxlint-disable-next-line no-console console.warn( - `[Sentry] Your \`Sentry.init()\` options include ${ignoredOptions.map(option => `\`${option}\``).join(' and ')}, which are currently ignored by the SDK. + `[Sentry] Your \`Sentry.init()\` options include ${ignoredOptions.join(' and ')}, which are currently ignored by the SDK! Use \`beforeSendSpan\` and \`ignoreSpans\` instead. Alternatively, set \`traceLifecycle: 'static'\` to opt out of streaming spans and keep the transaction-based options working.`, ); From 21fd1c9276c1791be527edfa10ef10554f3d268b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 31 Jul 2026 13:20:49 +0200 Subject: [PATCH 4/8] oxlint-disable --- packages/bundler-plugins/src/core/sentry/telemetry.ts | 2 +- packages/core/src/client.ts | 8 ++++++-- .../core/src/utils/warnAboutIgnoredTransactionOptions.ts | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/bundler-plugins/src/core/sentry/telemetry.ts b/packages/bundler-plugins/src/core/sentry/telemetry.ts index 6e26d5287e6d..962064c383ee 100644 --- a/packages/bundler-plugins/src/core/sentry/telemetry.ts +++ b/packages/bundler-plugins/src/core/sentry/telemetry.ts @@ -46,7 +46,7 @@ export function createSentryInstance( }, // Deprecated, but still applied because this client runs on the static trace lifecycle. - // eslint-disable-next-line typescript/no-deprecated + // oxlint-disable-next-line typescript/no-deprecated beforeSendTransaction: event => { delete event.server_name; // Server name might contain PII return event; diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 1e342941305e..a2d09f912b85 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -1656,8 +1656,12 @@ function processBeforeSend( event: Event, hint: EventHint, ): PromiseLike | Event | null { - // eslint-disable-next-line typescript/no-deprecated - const { beforeSend, beforeSendTransaction, ignoreSpans } = options; + const { + beforeSend, + ignoreSpans, + // oxlint-disable-next-line typescript/no-deprecated + beforeSendTransaction, + } = options; const beforeSendSpan = !isStreamedBeforeSendSpanCallback(options.beforeSendSpan) && options.beforeSendSpan; let processedEvent = event; diff --git a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts index 0af1ce0ecc76..cdbf0f9e6904 100644 --- a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts +++ b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts @@ -18,9 +18,9 @@ export function maybeWarnAboutIgnoredTransactionOptions(options: ClientOptions): const ignoredOptions = [ // oxlint-disable-next-line typescript/no-deprecated - options.beforeSendTransaction && '\`beforeSendTransaction\`', + options.beforeSendTransaction && '`beforeSendTransaction`', // oxlint-disable-next-line typescript/no-deprecated - options.ignoreTransactions?.length && '\`ignoreTransactions\`', + options.ignoreTransactions?.length && '`ignoreTransactions`', ].filter(Boolean); if (!ignoredOptions.length) { From a1f0fb16541002257320b8bf0ead38beafb502fe Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 31 Jul 2026 13:37:17 +0200 Subject: [PATCH 5/8] perf(core): Shrink the ignored-transaction-options warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The warning ships in every bundle, including the base CDN bundle that has no tracing at all, so its message text is paid for by users who will never see it. Measured against a build with the feature stubbed out, the original cost 204 B gzipped in the base CDN bundle. Almost all of that was the message: dropping the array/filter/join in favour of a single static string, and giving up naming which of the two options is actually set, brings it to 87 B — a 117 B saving. Naming the specific option was the expensive part, not the machinery that selected it; an interpolation-free ternary variant measured worse than the array it would have replaced. The static message still names both options, both replacements, and the opt-out, so it loses no actionable information. Co-Authored-By: Claude Opus 5 (1M context) --- .../warnAboutIgnoredTransactionOptions.ts | 22 +++++++----------- packages/core/test/lib/client.test.ts | 4 +--- ...warnAboutIgnoredTransactionOptions.test.ts | 23 ++++++++++++------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts index cdbf0f9e6904..5822b1ecd002 100644 --- a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts +++ b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts @@ -8,31 +8,25 @@ import { consoleSandbox } from './debug-logger'; * stop taking effect when users upgrade. Since that's easy to miss, this warning bypasses the `debug` logger * (which is opt-in and stripped from non-debug bundles) and writes to the console directly. * + * The message is one static string rather than one naming whichever option is set: it ships in every bundle, + * and interpolating the names costs more gzipped than the rest of this function put together. + * * Must be called after integrations are set up: `spanStreamingIntegration` may fall back to the static * trace lifecycle, in which case the options do take effect and we must stay silent. */ export function maybeWarnAboutIgnoredTransactionOptions(options: ClientOptions): void { - if (options.traceLifecycle !== 'stream') { - return; - } - - const ignoredOptions = [ - // oxlint-disable-next-line typescript/no-deprecated - options.beforeSendTransaction && '`beforeSendTransaction`', + if ( + options.traceLifecycle !== 'stream' || // oxlint-disable-next-line typescript/no-deprecated - options.ignoreTransactions?.length && '`ignoreTransactions`', - ].filter(Boolean); - - if (!ignoredOptions.length) { + !(options.beforeSendTransaction || options.ignoreTransactions?.length) + ) { return; } consoleSandbox(() => { // oxlint-disable-next-line no-console console.warn( - `[Sentry] Your \`Sentry.init()\` options include ${ignoredOptions.join(' and ')}, which are currently ignored by the SDK! -Use \`beforeSendSpan\` and \`ignoreSpans\` instead. -Alternatively, set \`traceLifecycle: 'static'\` to opt out of streaming spans and keep the transaction-based options working.`, + "[Sentry] `beforeSendTransaction` and `ignoreTransactions` are ignored with span streaming. Use `beforeSendSpan` and `ignoreSpans` instead, or set `traceLifecycle: 'static'`.", ); }); } diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index cb8d82663a02..7218a98dfbea 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -110,9 +110,7 @@ describe('Client', () => { expect(consoleWarnSpy).toHaveBeenCalledTimes(1); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining( - '`beforeSendTransaction` and `ignoreTransactions`, which are currently ignored by the SDK.', - ), + expect.stringContaining('`beforeSendTransaction` and `ignoreTransactions` are ignored with span streaming.'), ); consoleWarnSpy.mockRestore(); }); diff --git a/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts b/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts index 6e5feae64786..dadae7211872 100644 --- a/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts +++ b/packages/core/test/lib/utils/warnAboutIgnoredTransactionOptions.test.ts @@ -19,27 +19,25 @@ describe('maybeWarnAboutIgnoredTransactionOptions', () => { return { integrations: [], transport: () => ({}) as never, stackParser: () => [], ...overrides } as ClientOptions; } - it('warns about `beforeSendTransaction` when span streaming is enabled', () => { + it('warns when `beforeSendTransaction` is set and span streaming is enabled', () => { maybeWarnAboutIgnoredTransactionOptions( options({ traceLifecycle: 'stream', beforeSendTransaction: event => event }), ); expect(consoleWarnSpy).toHaveBeenCalledTimes(1); - expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`beforeSendTransaction`'); - expect(consoleWarnSpy.mock.calls[0]?.[0]).not.toContain('`ignoreTransactions`'); + expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`beforeSendTransaction` and `ignoreTransactions`'); }); - it('warns about `ignoreTransactions` when span streaming is enabled', () => { + it('warns when `ignoreTransactions` is set and span streaming is enabled', () => { maybeWarnAboutIgnoredTransactionOptions( options({ traceLifecycle: 'stream', ignoreTransactions: ['/healthcheck'] }), ); expect(consoleWarnSpy).toHaveBeenCalledTimes(1); - expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`ignoreTransactions`'); - expect(consoleWarnSpy.mock.calls[0]?.[0]).not.toContain('`beforeSendTransaction`'); + expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`beforeSendTransaction` and `ignoreTransactions`'); }); - it('warns once about both options when both are set', () => { + it('warns only once when both options are set', () => { maybeWarnAboutIgnoredTransactionOptions( options({ traceLifecycle: 'stream', @@ -49,7 +47,16 @@ describe('maybeWarnAboutIgnoredTransactionOptions', () => { ); expect(consoleWarnSpy).toHaveBeenCalledTimes(1); - expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain('`beforeSendTransaction` and `ignoreTransactions`'); + }); + + it('points at the replacement options and the opt-out', () => { + maybeWarnAboutIgnoredTransactionOptions( + options({ traceLifecycle: 'stream', beforeSendTransaction: event => event }), + ); + + const message = consoleWarnSpy.mock.calls[0]?.[0]; + expect(message).toContain('`beforeSendSpan` and `ignoreSpans`'); + expect(message).toContain("`traceLifecycle: 'static'`"); }); it('does not warn when the trace lifecycle is static', () => { From 450468ebed89a0fa80d476d490d2479f1978f9ac Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 31 Jul 2026 14:03:00 +0200 Subject: [PATCH 6/8] fix(core): Skip transaction-option warning when the client is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A disabled client (no DSN, or `enabled: false`) skips `_setupIntegrations()`, so `spanStreamingIntegration` never gets the chance to fall back to `traceLifecycle: 'static'`. The lifecycle stayed 'stream' and the warning fired, telling users their options were ignored because of span streaming — when the real reason was that the client sends nothing at all. The two paths were verifiably indistinguishable to the user: with an unwrapped `beforeSendSpan`, the identical config warns without a DSN and stays correctly silent with one, purely because setup ran in the second case. Move the call inside the branch that sets integrations up. The Spotlight escape hatch is handled correctly by construction: a DSN-less client with Spotlight does set up integrations and does send spans, so it should still warn. Co-Authored-By: Claude Opus 5 (1M context) --- packages/core/src/client.ts | 6 ++- .../warnAboutIgnoredTransactionOptions.ts | 5 ++- packages/core/test/lib/client.test.ts | 43 +++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index a2d09f912b85..303b04d125bb 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -508,9 +508,11 @@ export abstract class Client { this._options.integrations.some(({ name }) => name.startsWith('Spotlight')) ) { this._setupIntegrations(); - } - maybeWarnAboutIgnoredTransactionOptions(this._options); + // Inside this branch on purpose: integrations may switch `traceLifecycle` to 'static' during setup, + // and a client that never sets them up (disabled, no DSN) sends nothing, so it has nothing to ignore. + maybeWarnAboutIgnoredTransactionOptions(this._options); + } } /** diff --git a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts index 5822b1ecd002..7e71ee19e260 100644 --- a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts +++ b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts @@ -11,8 +11,9 @@ import { consoleSandbox } from './debug-logger'; * The message is one static string rather than one naming whichever option is set: it ships in every bundle, * and interpolating the names costs more gzipped than the rest of this function put together. * - * Must be called after integrations are set up: `spanStreamingIntegration` may fall back to the static - * trace lifecycle, in which case the options do take effect and we must stay silent. + * Must be called after integrations are set up, and only when they were set up at all: `spanStreamingIntegration` + * may fall back to the static trace lifecycle, in which case the options do take effect and we must stay silent. + * A client that skips integration setup never sends anything, so it ignores nothing worth warning about. */ export function maybeWarnAboutIgnoredTransactionOptions(options: ClientOptions): void { if ( diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index 7218a98dfbea..c6dc073fa92e 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -115,6 +115,49 @@ describe('Client', () => { consoleWarnSpy.mockRestore(); }); + test('stays silent when the client is disabled because no DSN was provided', () => { + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + + const options = getDefaultTestClientOptions({ + traceLifecycle: 'stream', + beforeSendTransaction: event => event, + ignoreTransactions: ['/healthcheck'], + }); + new TestClient(options).init(); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + consoleWarnSpy.mockRestore(); + }); + + test('stays silent when the client is disabled via `enabled: false`', () => { + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + + const options = getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + enabled: false, + traceLifecycle: 'stream', + beforeSendTransaction: event => event, + }); + new TestClient(options).init(); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + consoleWarnSpy.mockRestore(); + }); + + test('warns without a DSN when Spotlight is enabled, since spans are still sent', () => { + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + + const options = getDefaultTestClientOptions({ + traceLifecycle: 'stream', + beforeSendTransaction: event => event, + integrations: [{ name: 'SpotlightBrowser' }], + }); + new TestClient(options).init(); + + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + consoleWarnSpy.mockRestore(); + }); + test('stays silent when an integration falls back to the static trace lifecycle during setup', () => { const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); From 3545bc22a4afc420b7b53f6ec37de1a2e3e2fd19 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 31 Jul 2026 15:20:12 +0200 Subject: [PATCH 7/8] remove useless comments, size limit --- .size-limit.js | 2 +- packages/core/src/client.ts | 2 -- .../core/src/utils/warnAboutIgnoredTransactionOptions.ts | 7 ------- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index d589cfccdc65..6ef49dad2033 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -286,7 +286,7 @@ module.exports = [ path: createCDNPath('bundle.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '100 KB', + limit: '101 KB', disablePlugins: ['@size-limit/esbuild'], }, { diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 303b04d125bb..7196fd68f7f9 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -509,8 +509,6 @@ export abstract class Client { ) { this._setupIntegrations(); - // Inside this branch on purpose: integrations may switch `traceLifecycle` to 'static' during setup, - // and a client that never sets them up (disabled, no DSN) sends nothing, so it has nothing to ignore. maybeWarnAboutIgnoredTransactionOptions(this._options); } } diff --git a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts index 7e71ee19e260..9e9d3e808f1d 100644 --- a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts +++ b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts @@ -7,13 +7,6 @@ import { consoleSandbox } from './debug-logger'; * Both options are tied to the transaction event that span streaming no longer produces, so they silently * stop taking effect when users upgrade. Since that's easy to miss, this warning bypasses the `debug` logger * (which is opt-in and stripped from non-debug bundles) and writes to the console directly. - * - * The message is one static string rather than one naming whichever option is set: it ships in every bundle, - * and interpolating the names costs more gzipped than the rest of this function put together. - * - * Must be called after integrations are set up, and only when they were set up at all: `spanStreamingIntegration` - * may fall back to the static trace lifecycle, in which case the options do take effect and we must stay silent. - * A client that skips integration setup never sends anything, so it ignores nothing worth warning about. */ export function maybeWarnAboutIgnoredTransactionOptions(options: ClientOptions): void { if ( From 3b75af6ac9545d1baa6607be6e448e04c01f15af Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 31 Jul 2026 16:06:51 +0200 Subject: [PATCH 8/8] adjust warning --- packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts | 2 +- packages/core/test/lib/client.test.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts index 9e9d3e808f1d..f8fddb56c792 100644 --- a/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts +++ b/packages/core/src/utils/warnAboutIgnoredTransactionOptions.ts @@ -20,7 +20,7 @@ export function maybeWarnAboutIgnoredTransactionOptions(options: ClientOptions): consoleSandbox(() => { // oxlint-disable-next-line no-console console.warn( - "[Sentry] `beforeSendTransaction` and `ignoreTransactions` are ignored with span streaming. Use `beforeSendSpan` and `ignoreSpans` instead, or set `traceLifecycle: 'static'`.", + "[Sentry] `beforeSendTransaction` and `ignoreTransactions` are ignored with `traceLifecycle: 'stream'` (enabled by default). Use `beforeSendSpan` and `ignoreSpans` instead, or set `traceLifecycle: 'static'`.", ); }); } diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index c6dc073fa92e..d30314073bcf 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -110,7 +110,9 @@ describe('Client', () => { expect(consoleWarnSpy).toHaveBeenCalledTimes(1); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining('`beforeSendTransaction` and `ignoreTransactions` are ignored with span streaming.'), + expect.stringContaining( + "`beforeSendTransaction` and `ignoreTransactions` are ignored with `traceLifecycle: 'stream'` (enabled by default).", + ), ); consoleWarnSpy.mockRestore(); });