Skip to content

Event processor errors cause silent event loss via infinite recursion #19108

@thomasttvo

Description

@thomasttvo

Problem

Events are silently lost when an event processor throws an error. This is extremely hard to debug because there's no obvious indication. No events, no log, not even frozen UI, nothing, just HMR breaking, which doesn't seem related at first.

The root cause: when a processor throws, Sentry calls captureException to report it, which runs through the same processors, triggering infinite async recursion. The original event is never sent - it's buried under thousands of processor error events.

We spent significant time debugging why HMR was broken and discovered this.

Reproduction

Minimal repro repo: https://github.com/thomasttvo/sentry-recursion-repro

import * as Sentry from '@sentry/react-native';

Sentry.init({ dsn: '...' });

Sentry.addEventProcessor(async (event) => {
  throw new Error('Processor error'); // Throws on ALL events
});

Sentry.captureMessage('Test message'); // This event is LOST

What Happens

  1. captureMessage('Test') → processor throws → original event lost
  2. Sentry catches error → captureException(processorError) → processor throws again
  3. Repeat infinitely (700+ times in seconds, async so UI stays responsive)

The original "Test message" event never reaches Sentry. Instead, you get thousands of "Processor error" events (if they even make it through).

Impact

  1. Silent event loss - Original events never sent, no clear indication why
  2. Hard to debug - Took significant time to trace the issue
  3. Infinite async loop - Continuously consumes resources
  4. HMR breaks - In React Native dev mode, breaks Hot Module Replacement

Root Cause

In @sentry/core client.js:

.then(null, reason => {
  if (_isDoNotSendEventError(reason) || _isInternalError(reason)) {
    throw reason;
  }
  this.captureException(reason, { ... });  // Recurses!
  throw _makeInternalError(...);
})

Each iteration creates a NEW error, so existing guards don't prevent it.

Suggested Fix

Add recursion guard:

let _isProcessingProcessorError = false;

.then(null, reason => {
  if (_isProcessingProcessorError) {
    console.warn('Event processor error during error handling, breaking recursion');
    throw _makeInternalError(...);
  }
  _isProcessingProcessorError = true;
  try {
    this.captureException(reason, { ... });
  } finally {
    _isProcessingProcessorError = false;
  }
  throw _makeInternalError(...);
})

Environment

  • @sentry/react-native v7.11.0 (latest; issue exists in @sentry/core)
  • iOS / React Native / Expo SDK 54

Related Issues

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions