diff --git a/packages/core/src/utils/prepareEvent.ts b/packages/core/src/utils/prepareEvent.ts index 3a127d332686..6528873c3dee 100644 --- a/packages/core/src/utils/prepareEvent.ts +++ b/packages/core/src/utils/prepareEvent.ts @@ -11,6 +11,7 @@ import { addExceptionMechanism, uuid4 } from './misc'; import { normalize } from './normalize'; import { applyScopeDataToEvent, getCombinedScopeData } from './scopeData'; import { truncate } from './string'; +import { resolvedSyncPromise } from './syncpromise'; import { dateTimestampInSeconds } from './time'; /** @@ -93,7 +94,11 @@ export function prepareEvent( ...data.eventProcessors, ]; - const result = notifyEventProcessors(eventProcessors, prepared, hint); + // Skip event processors for internal exceptions to prevent recursion + const isInternalException = hint.data && (hint.data as { __sentry__: boolean }).__sentry__ === true; + const result = isInternalException + ? resolvedSyncPromise(prepared) + : notifyEventProcessors(eventProcessors, prepared, hint); return result.then(evt => { if (evt) { diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index 09ec34bf4fcc..21aab0ea609b 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -2007,6 +2007,31 @@ describe('Client', () => { }); }); + test('client-level event processor that throws on all events does not cause infinite recursion', () => { + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + const client = new TestClient(options); + + let processorCallCount = 0; + // Add processor at client level - this runs on ALL events including internal exceptions + client.addEventProcessor(() => { + processorCallCount++; + throw new Error('Processor always throws'); + }); + + client.captureMessage('test message'); + + // Should be called once for the original message + // internal exception events skips event processors entirely. + expect(processorCallCount).toBe(1); + + // Verify the processor error was captured and sent + expect(TestClient.instance!.event!.exception!.values![0]).toStrictEqual({ + type: 'Error', + value: 'Processor always throws', + mechanism: { type: 'internal', handled: false }, + }); + }); + test('records events dropped due to `sampleRate` option', () => { expect.assertions(1);