diff --git a/src/sentry/utils/sdk_crashes/sdk_crash_detection_config.py b/src/sentry/utils/sdk_crashes/sdk_crash_detection_config.py index 8897dcb00a87bc..d1e0d620791efc 100644 --- a/src/sentry/utils/sdk_crashes/sdk_crash_detection_config.py +++ b/src/sentry/utils/sdk_crashes/sdk_crash_detection_config.py @@ -236,6 +236,14 @@ def build_sdk_crash_detection_configs() -> Sequence[SDKCrashDetectionConfig]: module_pattern="@sentry/core/*/instrument/fetch*", function_pattern="fetch", ), + # The Supabase integration wraps PostgREST queries and captures errors from + # Supabase responses via captureException. The Error is constructed inside SDK + # code, so the stack trace only contains SDK frames, triggering false positives. + # https://github.com/getsentry/sentry-javascript/blob/10.47.0/packages/core/src/integrations/supabase.ts + FunctionAndModulePattern( + module_pattern="@sentry/core/*/integrations/supabase*", + function_pattern="Reflect.apply.then$argument_0", + ), }, ) configs.append(react_native_config) diff --git a/tests/sentry/utils/sdk_crashes/test_sdk_crash_detection_react_native.py b/tests/sentry/utils/sdk_crashes/test_sdk_crash_detection_react_native.py index 7f81505bfa61af..32dfb754e8645f 100644 --- a/tests/sentry/utils/sdk_crashes/test_sdk_crash_detection_react_native.py +++ b/tests/sentry/utils/sdk_crashes/test_sdk_crash_detection_react_native.py @@ -408,6 +408,83 @@ def test_fetch_instrumentation_not_detected( assert mock_sdk_crash_reporter.report.call_count == 0 +@pytest.mark.parametrize( + ["function", "module", "filename", "detected"], + [ + # Supabase PostgREST .then handler (ESM build) — should be ignored + ( + "Reflect.apply.then$argument_0", + "@sentry/core/build/esm/integrations/supabase", + "node_modules/@sentry/core/build/esm/integrations/supabase.js", + False, + ), + # Supabase PostgREST .then handler (CJS build) — should be ignored + ( + "Reflect.apply.then$argument_0", + "@sentry/core/build/cjs/integrations/supabase", + "node_modules/@sentry/core/build/cjs/integrations/supabase.js", + False, + ), + # Different function in the same module — should be detected + ( + "instrumentPostgRESTFilterBuilder", + "@sentry/core/build/esm/integrations/supabase", + "node_modules/@sentry/core/build/esm/integrations/supabase.js", + True, + ), + # Same function in a different module — should be detected + ( + "Reflect.apply.then$argument_0", + "@sentry/core/build/esm/integrations/graphql", + "node_modules/@sentry/core/build/esm/integrations/graphql.js", + True, + ), + ], +) +@decorators +def test_supabase_instrumentation_not_detected( + mock_sdk_crash_reporter, + mock_random, + store_event, + configs, + function: str, + module: str, + filename: str, + detected: bool, +) -> None: + event_data = get_crash_event( + exception={ + "values": [ + get_exception( + frames=[ + *get_frames(), + { + "function": function, + "module": module, + "filename": filename, + "abs_path": f"app:///{filename}", + }, + ], + ), + ] + } + ) + + event = store_event(data=event_data) + + configs[1].organization_allowlist = [event.project.organization_id] + + sdk_crash_detection.detect_sdk_crash( + event=event, + configs=configs, + ) + + if detected: + assert mock_sdk_crash_reporter.report.call_count == 1 + else: + assert mock_sdk_crash_reporter.report.call_count == 0 + + @decorators def test_sentry_wrapped_end_detected( mock_sdk_crash_reporter, mock_random, store_event, configs