Skip to content

TombstoneMerged events duplicate breadcrumbs and make Flutter/native scope data inconsistent #5713

Description

@zourw

Integration

sentry-android-ndk

Build System

Gradle

AGP Version

8.11.1

Proguard

Enabled

Other Error Monitoring Solution

No

Version

sentry_flutter 9.22.0 / sentry.native.android.flutter 0.15.2

Steps to Reproduce

  1. Create a Flutter Android app using sentry_flutter: ^9.22.0.
  2. Initialize Sentry with enableTombstone = true, attachThreads = true, enableUserInteractionBreadcrumbs = true, and enableAutoNativeBreadcrumbs = true.
  3. Wrap the app with SentryWidget.
  4. Trigger a real JNI/C++ SIGSEGV from a Flutter button via MethodChannel.
  5. Restart the app so the pending native/tombstone crash event is sent.
  6. Compare the regular native signalhandler event with the TombstoneMerged event in Sentry.

Flutter init:

await SentryFlutter.init(
  (options) {
    options.dsn = '<dsn>';
    options.release = 'sentry_demo@1.0.0+1';
    options.dist = '1';
    options.enableTombstone = true;
    options.attachThreads = true;
    options.enableUserInteractionBreadcrumbs = true;
    options.enableAutoNativeBreadcrumbs = true;
  },
  appRunner: () => runApp(SentryWidget(child: const App())),
);

Dart trigger:

static const _nativeChannel = MethodChannel('sentry_demo/native');

Future<void> triggerNativeCrash() async {
  await _nativeChannel.invokeMethod('triggerRealNativeCrash');
}

Android MainActivity.kt:

class MainActivity : FlutterActivity() {
    external fun triggerNativeSegfault()

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "sentry_demo/native")
            .setMethodCallHandler { call, result ->
                if (call.method == "triggerRealNativeCrash") {
                    triggerNativeSegfault()
                } else {
                    result.notImplemented()
                }
            }
    }

    companion object {
        init {
            System.loadLibrary("sentry_demo_native")
        }
    }
}

Native crash source:

#include <jni.h>

extern "C" JNIEXPORT void JNICALL
Java_com_example_sentry_1demo_MainActivity_triggerNativeSegfault(JNIEnv *, jobject) {
  volatile int *crash_address = nullptr;
  *crash_address = 1;
}

Environment:

  • Flutter app using sentry_flutter: ^9.22.0
  • Android Gradle Plugin: 8.11.1
  • Sentry Android Gradle Plugin: 6.14.0
  • Android 12 device
  • Native SIGSEGV triggered from JNI/C++
  • R8 enabled
  • No other error monitoring solution

Expected Result

A TombstoneMerged event should preserve the same relevant scope data as the regular native crash event, including breadcrumbs added by the Flutter SDK/native SDK, without duplicating entries.

In particular:

  • Flutter UI interaction breadcrumbs should be present once.
  • Navigation/app lifecycle/device breadcrumbs should be present once.
  • Custom breadcrumbs/tags/context already synced to the native scope before the crash should be preserved.
  • Enabling tombstone reporting should not make breadcrumbs less reliable than the regular native signal handler event.

Actual Result

When enableTombstone is enabled in a Flutter Android app, native SIGSEGV crashes are reported as TombstoneMerged events. Compared with regular native signalhandler events for the same crash path, the merged tombstone events preserve some breadcrumbs, but the scope/breadcrumb merge appears inconsistent:

  • The merged event contains duplicated breadcrumbs.
  • Some Flutter/Dart scope data is not reliable in the final tombstone merged event.
  • This makes the TombstoneMerged event less useful for investigating what happened before the crash.

Regular native crash event:

  • Issue: 131911518
  • Event ID: a0fc8e59e5a84631746cfb62af3cf072
  • mechanism: signalhandler
  • entries: exception, breadcrumbs, debugmeta
  • breadcrumbs count: 4
  • breadcrumbs include:
    • app.lifecycle
    • navigation
    • device.event
    • ui.click for the crash button

Tombstone merged crash events:

  • Issue: 131935719
  • Event IDs:
    • ba490da4525e4ec519f0fbf27c1966be
    • 89c1281cbb7045a8091131c05ef45a0b
  • mechanism: TombstoneMerged
  • entries: message, exception, threads, breadcrumbs, debugmeta
  • breadcrumbs count: 7
  • breadcrumbs include duplicated entries:
    • navigation appears twice
    • device.event appears twice
    • ui.click appears twice

Example from a TombstoneMerged event:

{
  "breadcrumbs": {
    "count": 7,
    "values": [
      { "category": "app.lifecycle", "data": { "state": "foreground" } },
      { "category": "navigation", "data": { "state": "didPush", "to": "home" } },
      { "category": "device.event", "data": { "action": "BATTERY_CHANGED", "charging": true, "level": 82 } },
      { "category": "ui.click", "data": { "label": "Android SIGSEGV", "view.id": "feature_button_Android SIGSEGV" } },
      { "category": "navigation", "data": { "state": "didPush", "to": "home" } },
      { "category": "device.event", "data": { "action": "BATTERY_CHANGED", "charging": true, "level": 82 } },
      { "category": "ui.click", "data": { "label": "Android SIGSEGV", "view.id": "feature_button_Android SIGSEGV" } }
    ]
  }
}

Regular signalhandler event breadcrumb summary:

2026-07-03T06:40:19.472Z app.lifecycle
2026-07-03T06:40:19.475Z navigation
2026-07-03T06:40:20.907Z device.event
2026-07-03T06:40:57.818Z ui.click Android SIGSEGV

TombstoneMerged event breadcrumb summary:

2026-07-03T08:34:36.806Z app.lifecycle
2026-07-03T08:34:36.810Z navigation
2026-07-03T08:34:37.612Z device.event
2026-07-03T08:34:52.788Z ui.click Android SIGSEGV
2026-07-03T08:34:36.810Z navigation
2026-07-03T08:34:37.612Z device.event
2026-07-03T08:34:52.788Z ui.click Android SIGSEGV

This looks related to the tombstone merge path. The same crash path reported through the regular native signalhandler event has a clean breadcrumb list. Once tombstone reporting is enabled and the event becomes TombstoneMerged, breadcrumbs are duplicated and scope data appears less predictable.

It looks like the merge process combines breadcrumbs from both the original native SDK event and the parsed tombstone/Java-side event without deduplicating, or replaces/merges event fields in a way that makes Flutter/native scope data inconsistent.

Metadata

Metadata

Assignees

No one assigned

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions