From 4d33e855fe044c7d5bc5e95453bb55c46aabc4d9 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Thu, 16 Jul 2026 12:01:11 +0200 Subject: [PATCH 1/7] feat(native): GA multi-exceptions --- relay-dynamic-config/src/feature.rs | 3 --- .../errors/errors/apple_crash_report.rs | 5 +---- .../src/processing/errors/errors/minidump.rs | 6 +---- .../processing/errors/errors/playstation.rs | 2 +- .../src/processing/errors/errors/unreal.rs | 5 ++--- relay-server/src/utils/native.rs | 22 +++++-------------- tests/integration/test_minidump.py | 14 ++++-------- 7 files changed, 14 insertions(+), 43 deletions(-) diff --git a/relay-dynamic-config/src/feature.rs b/relay-dynamic-config/src/feature.rs index ad069e7421d..c2372c726cc 100644 --- a/relay-dynamic-config/src/feature.rs +++ b/relay-dynamic-config/src/feature.rs @@ -102,9 +102,6 @@ pub enum Feature { /// Stream minidumps to objectstore. #[serde(rename = "projects:relay-minidump-uploads")] MinidumpUploads, - /// Allow additional exceptions to accompany minidumps. - #[serde(rename = "projects:minidump-multi-exception")] - MinidumpMultiException, /// Enable relay billing outcome generation. #[serde(rename = "organizations:relay-generate-billing-outcome")] GenerateBillingOutcome, diff --git a/relay-server/src/processing/errors/errors/apple_crash_report.rs b/relay-server/src/processing/errors/errors/apple_crash_report.rs index 3baa3b22412..2b6bd16ae12 100644 --- a/relay-server/src/processing/errors/errors/apple_crash_report.rs +++ b/relay-server/src/processing/errors/errors/apple_crash_report.rs @@ -26,10 +26,7 @@ impl SentryError for AppleCrashReport { let mut event = utils::take_event_from_crash_items(items, &mut metrics, ctx)?; utils::if_processing!(ctx, { - crate::utils::process_apple_crash_report( - event.get_or_insert_with(Default::default), - ctx.processing.project_info, - ); + crate::utils::process_apple_crash_report(event.get_or_insert_with(Default::default)); metrics.bytes_ingested_event_applecrashreport = (apple_crash_report.len() as u64).into(); }); diff --git a/relay-server/src/processing/errors/errors/minidump.rs b/relay-server/src/processing/errors/errors/minidump.rs index 45aa22fd041..b6f801f6305 100644 --- a/relay-server/src/processing/errors/errors/minidump.rs +++ b/relay-server/src/processing/errors/errors/minidump.rs @@ -26,11 +26,7 @@ impl SentryError for Minidump { let mut event = utils::take_event_from_crash_items(items, &mut metrics, ctx)?; utils::if_processing!(ctx, { - crate::utils::process_minidump( - event.get_or_insert_with(Default::default), - &minidump, - ctx.processing.project_info, - ); + crate::utils::process_minidump(event.get_or_insert_with(Default::default), &minidump); metrics.bytes_ingested_event_minidump = (minidump.attachment_body_size() as u64).into(); }); diff --git a/relay-server/src/processing/errors/errors/playstation.rs b/relay-server/src/processing/errors/errors/playstation.rs index 6c2f346c4f3..3363e07296e 100644 --- a/relay-server/src/processing/errors/errors/playstation.rs +++ b/relay-server/src/processing/errors/errors/playstation.rs @@ -107,7 +107,7 @@ impl SentryError for Playstation { // If the original prosperodump is already rate limited, so will be the minidump. item.set_rate_limited(prosperodump.rate_limited()); - crate::utils::process_minidump(event.get_or_insert_with(Event::default), &item, ctx.processing.project_info); + crate::utils::process_minidump(event.get_or_insert_with(Event::default), &item); item }; diff --git a/relay-server/src/processing/errors/errors/unreal.rs b/relay-server/src/processing/errors/errors/unreal.rs index 44af0186767..20d5f8b4c20 100644 --- a/relay-server/src/processing/errors/errors/unreal.rs +++ b/relay-server/src/processing/errors/errors/unreal.rs @@ -102,14 +102,13 @@ impl SentryError for Unreal { if let Some(minidump) = &minidump { crate::utils::process_minidump( event.get_or_insert_with(Default::default), - minidump, - ctx.processing.project_info + minidump ); metrics.bytes_ingested_event_minidump = (minidump.attachment_body_size() as u64).into(); } if let Some(acr) = &apple_crash_report { crate::utils::process_apple_crash_report( - event.get_or_insert_with(Default::default), ctx.processing.project_info + event.get_or_insert_with(Default::default) ); metrics.bytes_ingested_event_applecrashreport = (acr.len() as u64).into(); } diff --git a/relay-server/src/utils/native.rs b/relay-server/src/utils/native.rs index 141a92a1d6c..33b967abe52 100644 --- a/relay-server/src/utils/native.rs +++ b/relay-server/src/utils/native.rs @@ -10,7 +10,6 @@ use chrono::{TimeZone, Utc}; use minidump::{ MinidumpAnnotation, MinidumpCrashpadInfo, MinidumpModuleList, Module, StabilityReport, }; -use relay_dynamic_config::Feature; use relay_event_schema::protocol::{ ClientSdkInfo, Context, Contexts, Event, Exception, JsonLenientString, Level, Mechanism, StabilityReportContext, Values, @@ -18,7 +17,6 @@ use relay_event_schema::protocol::{ use relay_protocol::{Annotated, Value, get_value}; use crate::envelope::{Item, ItemType}; -use crate::services::projects::project::ProjectInfo; type Minidump<'a> = minidump::Minidump<'a, &'a [u8]>; @@ -45,11 +43,7 @@ struct NativePlaceholder { /// /// This will indicate to the ingestion pipeline that this event will need to be processed. The /// payload can be checked via `is_minidump_event`. -fn write_native_placeholder( - event: &mut Event, - placeholder: NativePlaceholder, - project_info: &ProjectInfo, -) { +fn write_native_placeholder(event: &mut Event, placeholder: NativePlaceholder) { // Events must be native platform. let platform = event.platform.value_mut(); *platform = Some("native".to_owned()); @@ -71,7 +65,6 @@ fn write_native_placeholder( .value_mut() .get_or_insert_with(Vec::new); - let allow_multiple_exceptions = project_info.has_feature(Feature::MinidumpMultiException); if let Some(exc) = exceptions.first() { relay_log::info!( additional_exceptions = exceptions.len(), @@ -79,16 +72,11 @@ fn write_native_placeholder( additional_exception_mechanism = ?get_value!(exc.mechanism.ty), sentry_project = ?event.project, event_id = ?event.id, - has_feature = allow_multiple_exceptions, platform = ?event.platform, "Native event has additional exceptions", ) } - if !allow_multiple_exceptions { - exceptions.clear(); // clear previous errors if any - } - // The placeholder for the minidump exception has to be the first in the list. This is what // sentry expects: https://github.com/getsentry/sentry/blob/f949db3155fcb6b79d3ee5e875b542460ed7c2c4/src/sentry/lang/native/utils.py#L149 exceptions.insert( @@ -223,14 +211,14 @@ fn write_crashpad_annotations( /// /// This function operates at best-effort. It always attaches the placeholder and returns /// successfully, even if the minidump or part of its data cannot be parsed. -pub fn process_minidump(event: &mut Event, item: &Item, project_info: &ProjectInfo) { +pub fn process_minidump(event: &mut Event, item: &Item) { debug_assert_eq!(item.ty(), &ItemType::Attachment); let placeholder = NativePlaceholder { exception_type: "Minidump", exception_value: "Invalid Minidump", mechanism_type: "minidump", }; - write_native_placeholder(event, placeholder, project_info); + write_native_placeholder(event, placeholder); if item.is_attachment_ref() { // We don't have a full minidump, just a placeholder for something that was uploaded @@ -293,11 +281,11 @@ pub fn process_minidump(event: &mut Event, item: &Item, project_info: &ProjectIn /// Writes minimal information into the event to indicate it is associated with an Apple Crash /// Report. -pub fn process_apple_crash_report(event: &mut Event, project_info: &ProjectInfo) { +pub fn process_apple_crash_report(event: &mut Event) { let placeholder = NativePlaceholder { exception_type: "AppleCrashReport", exception_value: "Invalid Apple Crash Report", mechanism_type: "applecrashreport", }; - write_native_placeholder(event, placeholder, project_info); + write_native_placeholder(event, placeholder); } diff --git a/tests/integration/test_minidump.py b/tests/integration/test_minidump.py index fde87229890..2b0a646f55a 100644 --- a/tests/integration/test_minidump.py +++ b/tests/integration/test_minidump.py @@ -714,9 +714,8 @@ def test_minidump_with_processing_invalid( ] -@pytest.mark.parametrize("feature_flag", [False, True]) def test_minidump_with_event_exception( - mini_sentry, relay_with_processing, attachments_consumer, feature_flag + mini_sentry, relay_with_processing, attachments_consumer ): """ An envelope can carry both a minidump attachment and an event item that already @@ -732,8 +731,6 @@ def test_minidump_with_event_exception( project_id = 42 project_config = mini_sentry.add_full_project_config(project_id) config = project_config["config"] - if feature_flag: - config.setdefault("features", []).append("projects:minidump-multi-exception") # Disable scrubbing, the basic and full project configs from the mini_sentry fixture # will modify the minidump since it contains user paths in the module list. @@ -791,12 +788,9 @@ def test_minidump_with_event_exception( assert minidump_exception["mechanism"]["type"] == "minidump" # The user-provided exception with its stack trace must be preserved. - if feature_flag: - (user_exception,) = additional_exceptions - assert user_exception["value"] == "division by zero" - assert user_exception["stacktrace"]["frames"][0]["function"] == "divide" - else: - assert not additional_exceptions + (user_exception,) = additional_exceptions + assert user_exception["value"] == "division by zero" + assert user_exception["stacktrace"]["frames"][0]["function"] == "divide" # The minidump must still be forwarded as an attachment. assert any(att["name"] == "minidump.dmp" for att in message["attachments"]) From 903f641dd3898281f849c0fcacd75370bcead55f Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Thu, 16 Jul 2026 13:51:22 +0200 Subject: [PATCH 2/7] fix: keep deleting for unreal and playstation --- .../src/processing/errors/errors/minidump.rs | 8 +++++- .../processing/errors/errors/playstation.rs | 3 ++- .../src/processing/errors/errors/unreal.rs | 4 ++- relay-server/src/utils/native.rs | 27 ++++++++++++++++--- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/relay-server/src/processing/errors/errors/minidump.rs b/relay-server/src/processing/errors/errors/minidump.rs index b6f801f6305..502d79ce0ef 100644 --- a/relay-server/src/processing/errors/errors/minidump.rs +++ b/relay-server/src/processing/errors/errors/minidump.rs @@ -5,6 +5,8 @@ use crate::managed::{Counted, Quantities, RecordKeeper}; use crate::processing::ForwardContext; use crate::processing::errors::errors::{Context, Expansion, SentryError, utils}; use crate::processing::errors::{Error, Result}; +#[cfg(feature = "processing")] +use crate::utils::AdditionalExceptions; #[derive(Debug)] pub struct Minidump(pub Item); @@ -26,7 +28,11 @@ impl SentryError for Minidump { let mut event = utils::take_event_from_crash_items(items, &mut metrics, ctx)?; utils::if_processing!(ctx, { - crate::utils::process_minidump(event.get_or_insert_with(Default::default), &minidump); + crate::utils::process_minidump( + event.get_or_insert_with(Default::default), + &minidump, + AdditionalExceptions::Retain, + ); metrics.bytes_ingested_event_minidump = (minidump.attachment_body_size() as u64).into(); }); diff --git a/relay-server/src/processing/errors/errors/playstation.rs b/relay-server/src/processing/errors/errors/playstation.rs index 3363e07296e..da1242d4d38 100644 --- a/relay-server/src/processing/errors/errors/playstation.rs +++ b/relay-server/src/processing/errors/errors/playstation.rs @@ -30,6 +30,7 @@ impl SentryError for Playstation { fn try_expand(items: &mut Vec, ctx: Context<'_>) -> Result>> { use crate::envelope::{AttachmentType, ItemType}; use crate::processing::errors::errors::utils; + use crate::utils::AdditionalExceptions; use relay_dynamic_config::Feature; use relay_event_schema::protocol::Metrics; @@ -107,7 +108,7 @@ impl SentryError for Playstation { // If the original prosperodump is already rate limited, so will be the minidump. item.set_rate_limited(prosperodump.rate_limited()); - crate::utils::process_minidump(event.get_or_insert_with(Event::default), &item); + crate::utils::process_minidump(event.get_or_insert_with(Event::default), &item, AdditionalExceptions::Delete); item }; diff --git a/relay-server/src/processing/errors/errors/unreal.rs b/relay-server/src/processing/errors/errors/unreal.rs index 20d5f8b4c20..84885224ac1 100644 --- a/relay-server/src/processing/errors/errors/unreal.rs +++ b/relay-server/src/processing/errors/errors/unreal.rs @@ -5,6 +5,8 @@ use crate::managed::{Counted, Quantities, RecordKeeper}; use crate::processing::ForwardContext; use crate::processing::errors::Result; use crate::processing::errors::errors::{Context, Expansion, SentryError, utils}; +#[cfg(feature = "processing")] +use crate::utils::AdditionalExceptions; #[derive(Debug)] pub enum UnrealReport { @@ -102,7 +104,7 @@ impl SentryError for Unreal { if let Some(minidump) = &minidump { crate::utils::process_minidump( event.get_or_insert_with(Default::default), - minidump + minidump, AdditionalExceptions::Delete ); metrics.bytes_ingested_event_minidump = (minidump.attachment_body_size() as u64).into(); } diff --git a/relay-server/src/utils/native.rs b/relay-server/src/utils/native.rs index 33b967abe52..155a52ce224 100644 --- a/relay-server/src/utils/native.rs +++ b/relay-server/src/utils/native.rs @@ -38,12 +38,23 @@ struct NativePlaceholder { mechanism_type: &'static str, } +#[derive(Clone, Copy)] +/// What to do with additional exceptions in a minidump / apple crash report event. +pub enum AdditionalExceptions { + Retain, + Delete, +} + /// Writes a placeholder to indicate that this event has an associated minidump or an apple /// crash report. /// /// This will indicate to the ingestion pipeline that this event will need to be processed. The /// payload can be checked via `is_minidump_event`. -fn write_native_placeholder(event: &mut Event, placeholder: NativePlaceholder) { +fn write_native_placeholder( + event: &mut Event, + placeholder: NativePlaceholder, + additional_exceptions: AdditionalExceptions, +) { // Events must be native platform. let platform = event.platform.value_mut(); *platform = Some("native".to_owned()); @@ -77,6 +88,10 @@ fn write_native_placeholder(event: &mut Event, placeholder: NativePlaceholder) { ) } + if matches!(additional_exceptions, AdditionalExceptions::Delete) { + exceptions.clear(); // clear previous errors if any + } + // The placeholder for the minidump exception has to be the first in the list. This is what // sentry expects: https://github.com/getsentry/sentry/blob/f949db3155fcb6b79d3ee5e875b542460ed7c2c4/src/sentry/lang/native/utils.py#L149 exceptions.insert( @@ -211,14 +226,18 @@ fn write_crashpad_annotations( /// /// This function operates at best-effort. It always attaches the placeholder and returns /// successfully, even if the minidump or part of its data cannot be parsed. -pub fn process_minidump(event: &mut Event, item: &Item) { +pub fn process_minidump( + event: &mut Event, + item: &Item, + additional_exceptions: AdditionalExceptions, +) { debug_assert_eq!(item.ty(), &ItemType::Attachment); let placeholder = NativePlaceholder { exception_type: "Minidump", exception_value: "Invalid Minidump", mechanism_type: "minidump", }; - write_native_placeholder(event, placeholder); + write_native_placeholder(event, placeholder, additional_exceptions); if item.is_attachment_ref() { // We don't have a full minidump, just a placeholder for something that was uploaded @@ -287,5 +306,5 @@ pub fn process_apple_crash_report(event: &mut Event) { exception_value: "Invalid Apple Crash Report", mechanism_type: "applecrashreport", }; - write_native_placeholder(event, placeholder); + write_native_placeholder(event, placeholder, AdditionalExceptions::Retain); } From b4c86ea200d33b0347b424610bb15471ab19f721 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Thu, 16 Jul 2026 13:54:25 +0200 Subject: [PATCH 3/7] doc: changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 344b1341321..f2770b4be40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +**Features**: + +- Allow additional exceptions in minidump and apple crash report events. ([#6241](https://github.com/getsentry/relay/pull/6241)) + ## 26.7.0 **Features**: From d118defb54ce5458cfb2499481fe4b12bb43c171 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Thu, 16 Jul 2026 14:14:22 +0200 Subject: [PATCH 4/7] fix: cfg(sentry) --- relay-server/src/processing/errors/errors/playstation.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/relay-server/src/processing/errors/errors/playstation.rs b/relay-server/src/processing/errors/errors/playstation.rs index da1242d4d38..3363e07296e 100644 --- a/relay-server/src/processing/errors/errors/playstation.rs +++ b/relay-server/src/processing/errors/errors/playstation.rs @@ -30,7 +30,6 @@ impl SentryError for Playstation { fn try_expand(items: &mut Vec, ctx: Context<'_>) -> Result>> { use crate::envelope::{AttachmentType, ItemType}; use crate::processing::errors::errors::utils; - use crate::utils::AdditionalExceptions; use relay_dynamic_config::Feature; use relay_event_schema::protocol::Metrics; @@ -108,7 +107,7 @@ impl SentryError for Playstation { // If the original prosperodump is already rate limited, so will be the minidump. item.set_rate_limited(prosperodump.rate_limited()); - crate::utils::process_minidump(event.get_or_insert_with(Event::default), &item, AdditionalExceptions::Delete); + crate::utils::process_minidump(event.get_or_insert_with(Event::default), &item); item }; From d6c4046fc678fbd26f0b98bfff44b286c3e5b674 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Thu, 16 Jul 2026 14:36:08 +0200 Subject: [PATCH 5/7] fix again --- relay-server/src/processing/errors/errors/playstation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relay-server/src/processing/errors/errors/playstation.rs b/relay-server/src/processing/errors/errors/playstation.rs index 3363e07296e..30b3d3b0587 100644 --- a/relay-server/src/processing/errors/errors/playstation.rs +++ b/relay-server/src/processing/errors/errors/playstation.rs @@ -107,7 +107,7 @@ impl SentryError for Playstation { // If the original prosperodump is already rate limited, so will be the minidump. item.set_rate_limited(prosperodump.rate_limited()); - crate::utils::process_minidump(event.get_or_insert_with(Event::default), &item); + crate::utils::process_minidump(event.get_or_insert_with(Event::default), &item, crate::utils::AdditionalExceptions::Delete); item }; From 07ace2350b526ce57361b9a9b4f50bfa41ed80d1 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Thu, 16 Jul 2026 16:20:26 +0200 Subject: [PATCH 6/7] review --- .../src/processing/errors/errors/apple_crash_report.rs | 6 +++++- relay-server/src/processing/errors/errors/unreal.rs | 2 +- relay-server/src/utils/native.rs | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/relay-server/src/processing/errors/errors/apple_crash_report.rs b/relay-server/src/processing/errors/errors/apple_crash_report.rs index 2b6bd16ae12..1bcd892a656 100644 --- a/relay-server/src/processing/errors/errors/apple_crash_report.rs +++ b/relay-server/src/processing/errors/errors/apple_crash_report.rs @@ -5,6 +5,7 @@ use crate::managed::{Counted, Quantities, RecordKeeper}; use crate::processing::ForwardContext; use crate::processing::errors::errors::{Context, Expansion, SentryError, utils}; use crate::processing::errors::{Error, Result}; +use crate::utils::AdditionalExceptions; #[derive(Debug)] pub struct AppleCrashReport(pub Item); @@ -26,7 +27,10 @@ impl SentryError for AppleCrashReport { let mut event = utils::take_event_from_crash_items(items, &mut metrics, ctx)?; utils::if_processing!(ctx, { - crate::utils::process_apple_crash_report(event.get_or_insert_with(Default::default)); + crate::utils::process_apple_crash_report( + event.get_or_insert_with(Default::default), + AdditionalExceptions::Retain, + ); metrics.bytes_ingested_event_applecrashreport = (apple_crash_report.len() as u64).into(); }); diff --git a/relay-server/src/processing/errors/errors/unreal.rs b/relay-server/src/processing/errors/errors/unreal.rs index 84885224ac1..e0eb89fe979 100644 --- a/relay-server/src/processing/errors/errors/unreal.rs +++ b/relay-server/src/processing/errors/errors/unreal.rs @@ -110,7 +110,7 @@ impl SentryError for Unreal { } if let Some(acr) = &apple_crash_report { crate::utils::process_apple_crash_report( - event.get_or_insert_with(Default::default) + event.get_or_insert_with(Default::default), AdditionalExceptions::Delete ); metrics.bytes_ingested_event_applecrashreport = (acr.len() as u64).into(); } diff --git a/relay-server/src/utils/native.rs b/relay-server/src/utils/native.rs index 155a52ce224..e54236fecca 100644 --- a/relay-server/src/utils/native.rs +++ b/relay-server/src/utils/native.rs @@ -300,11 +300,11 @@ pub fn process_minidump( /// Writes minimal information into the event to indicate it is associated with an Apple Crash /// Report. -pub fn process_apple_crash_report(event: &mut Event) { +pub fn process_apple_crash_report(event: &mut Event, additional_exceptions: AdditionalExceptions) { let placeholder = NativePlaceholder { exception_type: "AppleCrashReport", exception_value: "Invalid Apple Crash Report", mechanism_type: "applecrashreport", }; - write_native_placeholder(event, placeholder, AdditionalExceptions::Retain); + write_native_placeholder(event, placeholder, additional_exceptions); } From f9678e07ebc687358e809bdc2e9b6f3a28c2bee9 Mon Sep 17 00:00:00 2001 From: Joris Bayer Date: Thu, 16 Jul 2026 16:35:51 +0200 Subject: [PATCH 7/7] lint --- .../src/processing/errors/errors/apple_crash_report.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/relay-server/src/processing/errors/errors/apple_crash_report.rs b/relay-server/src/processing/errors/errors/apple_crash_report.rs index 1bcd892a656..e3f3d2a0155 100644 --- a/relay-server/src/processing/errors/errors/apple_crash_report.rs +++ b/relay-server/src/processing/errors/errors/apple_crash_report.rs @@ -5,7 +5,6 @@ use crate::managed::{Counted, Quantities, RecordKeeper}; use crate::processing::ForwardContext; use crate::processing::errors::errors::{Context, Expansion, SentryError, utils}; use crate::processing::errors::{Error, Result}; -use crate::utils::AdditionalExceptions; #[derive(Debug)] pub struct AppleCrashReport(pub Item); @@ -29,7 +28,7 @@ impl SentryError for AppleCrashReport { utils::if_processing!(ctx, { crate::utils::process_apple_crash_report( event.get_or_insert_with(Default::default), - AdditionalExceptions::Retain, + crate::utils::AdditionalExceptions::Retain, ); metrics.bytes_ingested_event_applecrashreport = (apple_crash_report.len() as u64).into();