@@ -195,15 +195,15 @@ private static CocoaSdk.SentryHttpStatusCodeRange[] GetFailedRequestStatusCodes(
195195 => ProcessOnBeforeSend ( options , evt , CurrentHub ) ;
196196
197197 /// <summary>
198- /// This overload allows us to inject an IHub for testing. During normal execution, the CurrentHub is used.
199- /// However, since this class is static, there's no easy alternative way to inject this when executing tests.
198+ /// Apply suppression logic for redundant native `SIGABRT` and `EXC_BAD_ACCESS` crash events
199+ /// that have already been captured as managed exceptions by the Sentry.NET SDK to avoid sending
200+ /// duplicate events to Sentry - once managed and once native.
201+ ///
202+ /// The managed exception is what a .NET developer would expect, and it is sent by the Sentry.NET SDK
203+ /// But we also get a native SIGABRT since it crashed the application, which is sent by the Sentry Cocoa SDK.
200204 /// </summary>
201- internal static CocoaSdk . SentryEvent ? ProcessOnBeforeSend ( SentryOptions options , CocoaSdk . SentryEvent evt , IHub hub )
205+ private static bool SuppressNativeCrash ( SentryOptions options , CocoaSdk . SentryEvent evt )
202206 {
203- // When we have an unhandled managed exception, we send that to Sentry twice - once managed and once native.
204- // The managed exception is what a .NET developer would expect, and it is sent by the Sentry.NET SDK
205- // But we also get a native SIGABRT since it crashed the application, which is sent by the Sentry Cocoa SDK.
206-
207207 // There should only be one exception on the event in this case
208208 if ( ( options . Native . SuppressSignalAborts || options . Native . SuppressExcBadAccess ) && evt . Exceptions ? . Length == 1 )
209209 {
@@ -216,24 +216,39 @@ private static CocoaSdk.SentryHttpStatusCodeRange[] GetFailedRequestStatusCodes(
216216 if ( options . Native . SuppressSignalAborts && ex . Type == "SIGABRT" && ex . Value == "Signal 6, Code 0" &&
217217 ex . Stacktrace ? . Frames . Any ( f => f . Function == "xamarin_unhandled_exception_handler" ) is true )
218218 {
219- // Don't send it
220- options . LogDebug ( "Discarded {0} error ({1}). Captured as managed exception instead." , ex . Type ,
221- ex . Value ) ;
222- return null ! ;
219+ return true ;
223220 }
224221
225222 // Similar workaround for NullReferenceExceptions. We don't have any easy way to know whether the
226223 // exception is managed code (compiled to native) or original native code though.
227224 // See: https://github.com/getsentry/sentry-dotnet/issues/3776
228225 if ( options . Native . SuppressExcBadAccess && ex . Type == "EXC_BAD_ACCESS" )
229226 {
230- // Don't send it
231- options . LogDebug ( "Discarded {0} error ({1}). Captured as managed exception instead." , ex . Type ,
232- ex . Value ) ;
233- return null ! ;
227+ return true ;
234228 }
235229 }
236230
231+ return false ;
232+ }
233+
234+ /// <summary>
235+ /// This overload allows us to inject an IHub for testing. During normal execution, the CurrentHub is used.
236+ /// However, since this class is static, there's no easy alternative way to inject this when executing tests.
237+ /// </summary>
238+ internal static CocoaSdk . SentryEvent ? ProcessOnBeforeSend ( SentryOptions options , CocoaSdk . SentryEvent evt , IHub hub )
239+ {
240+ // Redundant native crash events must be suppressed even if the SDK is
241+ // disabled (or not yet fully initialized) to avoid sending duplicates.
242+ // https://github.com/getsentry/sentry-dotnet/pull/4521#discussion_r2347616896
243+ if ( SuppressNativeCrash ( options , evt ) )
244+ {
245+ // Don't send it
246+ options . LogDebug ( "Discarded {0} error ({1}). Captured as managed exception instead." , ex . Type ,
247+ ex . Value ) ;
248+ return null ! ;
249+ }
250+
251+ // If the SDK is disabled, there are no event processors or before send to run.
237252 if ( hub is DisabledHub )
238253 {
239254 return evt ;
0 commit comments