Skip to content

Conversation

@calm329
Copy link

@calm329 calm329 commented Jan 6, 2026

Fixes #105625

Summary

The SDK crash detection was incorrectly reporting crashes as SDK crashes when SentrySwizzleWrapper was the only SDK frame in the stacktrace. These are false positives because SentrySwizzleWrapper is an instrumentation frame that intercepts UI events but doesn't cause crashes itself.

This PR adds a new conditional SDK frame detection mechanism that only ignores SentrySwizzleWrapper when there are no other SDK frames above it (closer to the crash origin).

Changes

  • Added sdk_crash_ignore_when_only_sdk_frame_matchers field to SDKCrashDetectionConfig
  • Modified is_sdk_crash() in SDKCrashDetector to handle conditional SDK frames
  • Added SentrySwizzleWrapper pattern to Cocoa SDK configuration
  • Added CocoaSDKSwizzleWrapperTestMixin with 3 test cases:
    • SwizzleWrapper as only SDK frame → NOT reported
    • SwizzleWrapper with SDK frames above → IS reported
    • SwizzleWrapper with SDK frames below → NOT reported

How it works

When iterating through frames (youngest to oldest):

  1. If a non-conditional SDK frame is found → return True (SDK crash)
  2. If a conditional SDK frame (like SwizzleWrapper) is found → return False (not SDK crash, since any SDK frames above would have triggered step 1)
  3. If a non-system frame is found → return False

Legal Boilerplate

Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.


Contribution by Gittensor, see my contribution statistics at https://gittensor.io/miners/details?githubId=148254234

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Jan 6, 2026
@calm329
Copy link
Author

calm329 commented Jan 7, 2026

@philipphofmann I’ve opened this PR and would appreciate a review when you have a chance. Thanks!

Copy link
Member

@philipphofmann philipphofmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for tackling this @calm329. I added a few comments.

Comment on lines 785 to 816
# This SDK frame is BELOW SentrySwizzleWrapper (further from crash origin)
"function": "-[SentryHub captureEvent:]",
"package": "/private/var/containers/Bundle/Application/59E988EF-46DB-4C75-8E08-10C27DC3E90E/iOS-Swift.app/Frameworks/Sentry.framework/Sentry",
"in_app": False,
},
{
"function": "-[UIApplication sendEvent:]",
"package": "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore",
"in_app": False,
},
{
"function": "__49-[SentrySwizzleWrapper swizzleSendAction:forKey:]_block_invoke_2",
"package": "/private/var/containers/Bundle/Application/59E988EF-46DB-4C75-8E08-10C27DC3E90E/iOS-Swift.app/Frameworks/Sentry.framework/Sentry",
"in_app": False,
},
{
"function": "-[NSString substringWithRange:]",
"package": "/System/Library/Frameworks/Foundation.framework/Foundation",
"in_app": False,
},
{
"function": "__exceptionPreprocess",
"package": "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation",
"in_app": False,
},
]

self.execute_test(
get_crash_event_with_frames(frames),
False, # Should NOT be reported - SDK frame below doesn't count
mock_sdk_crash_reporter,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I could have been more specific in #105625. We should rather overreport than underreport SDK crashes. I think we should still report that crash as an SDK crash. I think the logic should be it's OK to ignore the SDK crash if the specific function is the only SDK frame in the whole stacktrace.

@calm329
Copy link
Author

calm329 commented Jan 9, 2026

@philipphofmann I've fixed according to your feedback, could you review it again?

Copy link
Member

@philipphofmann philipphofmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for including the feedback. We're not far away from an approval, @calm329 💯

If there are other SDK frames above these frames, the crash is still reported as an SDK crash.
For example, SentrySwizzleWrapper is used for method swizzling and shouldn't be reported as an SDK crash
when it's the only SDK frame, since the actual crash originates from system libraries."""
These frames are typically SDK instrumentation frames that intercept calls, such as swizzling or monkey patching,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's better thanks

Comment on lines 125 to 156
def _matches_ignore_when_only_sdk_frame(self, frame: Mapping[str, Any]) -> bool:
"""
Returns true if the frame matches the sdk_crash_ignore_when_only_sdk_frame_matchers pattern.
"""
function = frame.get("function")
if not function:
return False

module = frame.get("module")
for matcher in self.config.sdk_crash_ignore_when_only_sdk_frame_matchers:
function_matches = glob_match(function, matcher.function_pattern, ignorecase=True)
module_matches = glob_match(module, matcher.module_pattern, ignorecase=True)

if function_matches and module_matches:
return True

return False

def _matches_sdk_crash_ignore(self, frame: Mapping[str, Any]) -> bool:
function = frame.get("function")
if not function:
return False

module = frame.get("module")
for matcher in self.config.sdk_crash_ignore_matchers:
function_matches = glob_match(function, matcher.function_pattern, ignorecase=True)
module_matches = glob_match(module, matcher.module_pattern, ignorecase=True)

if function_matches and module_matches:
return True

return False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m: These two methods are almost the same. It would be great to get rid of the code duplication, please.

)


@patch("sentry.utils.sdk_crashes.sdk_crash_detection.sdk_crash_detection.sdk_crash_reporter")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tests.

Comment on lines +91 to +99
has_non_conditional_sdk_frame = False
for frame in iter_frames:
if (
self.is_sdk_frame(frame)
and not self._matches_ignore_when_only_sdk_frame(frame)
and not self._matches_sdk_crash_ignore(frame)
):
has_non_conditional_sdk_frame = True
break
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m: I find this a bit confusing now. We have a method called _matches_sdk_crash_ignore, but we don't use it below. We still have code that calls for matcher in self.config.sdk_crash_ignore_matchers below. This code for matcher in self.config.sdk_crash_ignore_matchers below should also use the _matches_sdk_crash_ignore method please.

# are SDK frames or from system libraries.
iter_frames = [f for f in reversed(frames) if f is not None]

has_non_conditional_sdk_frame = False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m: I think this code could more clearly communicate what it does. We can iterate over all frames 3 times, so it's still O(n), where n is the number of frames.
First, I would iterate over all frames to check if we should ignore the crash altogether, because a frame matches the _matches_sdk_crash_ignore. If it does, we ignore the SDK crash.

Second, I would iterate over all frames to check if a grame matches the _matches_ignore_when_only_sdk_frame. This loop keeps track if it's the only SDK frame that matches this condidition. If it does then we exit and say, no SDK crash.

The third loop performs the remaining checks to determine whether it's an SDK crash. Then we have the logic seperated into three different for loops and they don't overlap with each other in one for loop, which makes it now harder to understand.

Comment on lines 95 to 99
has_non_conditional_sdk_frame = False
for frame in iter_frames:
if self.is_sdk_frame(frame) and not self._matches_ignore_when_only_sdk_frame(frame):
has_non_conditional_sdk_frame = True
break

This comment was marked as outdated.

Comment on lines 107 to 108
if self._matches_sdk_crash_ignore(frame):
return False

This comment was marked as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SDK Crashes: Ignore false positive for SentrySwizzleWrapper

2 participants