diff --git a/src/sentry/api/endpoints/project_details.py b/src/sentry/api/endpoints/project_details.py
index eeaf7baf282e6d..362f3ebbd8d747 100644
--- a/src/sentry/api/endpoints/project_details.py
+++ b/src/sentry/api/endpoints/project_details.py
@@ -695,6 +695,11 @@ def put(self, request: Request, project) -> Response:
"sentry:reprocessing_active",
bool(options["sentry:reprocessing_active"]),
)
+ if "filters:react-hydration-errors" in options:
+ project.update_option(
+ "filters:react-hydration-errors",
+ bool(options["filters:react-hydration-errors"]),
+ )
if "filters:blacklisted_ips" in options:
project.update_option(
"sentry:blacklisted_ips",
diff --git a/src/sentry/api/serializers/models/project.py b/src/sentry/api/serializers/models/project.py
index be2f4b2e188575..fbaf131cef3351 100644
--- a/src/sentry/api/serializers/models/project.py
+++ b/src/sentry/api/serializers/models/project.py
@@ -176,6 +176,7 @@ def format_options(attrs: defaultdict(dict)):
"sentry:performance_issue_creation_rate"
),
"filters:blacklisted_ips": "\n".join(options.get("sentry:blacklisted_ips", [])),
+ "filters:react-hydration-errors": bool(options.get("filters:react-hydration-errors", True)),
f"filters:{FilterTypes.RELEASES}": "\n".join(
options.get(f"sentry:{FilterTypes.RELEASES}", [])
),
diff --git a/src/sentry/relay/config/__init__.py b/src/sentry/relay/config/__init__.py
index e31ac182eaceab..4afb13c94953bb 100644
--- a/src/sentry/relay/config/__init__.py
+++ b/src/sentry/relay/config/__init__.py
@@ -109,14 +109,31 @@ def get_filter_settings(project: Project) -> Mapping[str, Any]:
settings = _load_filter_settings(flt, project)
filter_settings[filter_id] = settings
+ error_messages = []
if features.has("projects:custom-inbound-filters", project):
invalid_releases = project.get_option(f"sentry:{FilterTypes.RELEASES}")
if invalid_releases:
filter_settings["releases"] = {"releases": invalid_releases}
- error_messages = project.get_option(f"sentry:{FilterTypes.ERROR_MESSAGES}")
- if error_messages:
- filter_settings["errorMessages"] = {"patterns": error_messages}
+ error_messages += project.get_option(f"sentry:{FilterTypes.ERROR_MESSAGES}") or []
+
+ enable_react = project.get_option("filters:react-hydration-errors")
+ if enable_react:
+ error_messages += [
+ # Hydration failed because the initial UI does not match what was rendered on the server.
+ "https://reactjs.org/docs/error-decoder.html?invariant=418",
+ # The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.
+ "https://reactjs.org/docs/error-decoder.html?invariant=419",
+ # There was an error while hydrating this Suspense boundary. Switched to client rendering.
+ "https://reactjs.org/docs/error-decoder.html?invariant=422",
+ # There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
+ "https://reactjs.org/docs/error-decoder.html?invariant=423",
+ # Text content does not match server-rendered HTML.
+ "https://reactjs.org/docs/error-decoder.html?invariant=425",
+ ]
+
+ if error_messages:
+ filter_settings["errorMessages"] = {"patterns": error_messages}
blacklisted_ips = project.get_option("sentry:blacklisted_ips")
if blacklisted_ips:
diff --git a/static/app/data/forms/inboundFilters.tsx b/static/app/data/forms/inboundFilters.tsx
index b035bc029777f2..5b157b6543f849 100644
--- a/static/app/data/forms/inboundFilters.tsx
+++ b/static/app/data/forms/inboundFilters.tsx
@@ -12,7 +12,7 @@ const globHelpText = tct('Allows [link:glob pattern matching].', {
link: ,
});
-const getOptionsData = (data: object) => ({options: data});
+export const getOptionsData = (data: object) => ({options: data});
const formGroups: JsonFormObject[] = [
{
diff --git a/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx b/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx
index 68a9c420cbd576..00b3fe1df0143f 100644
--- a/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx
+++ b/static/app/views/settings/project/projectFilters/projectFiltersSettings.tsx
@@ -23,7 +23,10 @@ import {
PanelItem,
} from 'sentry/components/panels';
import Switch from 'sentry/components/switchButton';
-import filterGroups, {customFilterFields} from 'sentry/data/forms/inboundFilters';
+import filterGroups, {
+ customFilterFields,
+ getOptionsData,
+} from 'sentry/data/forms/inboundFilters';
import {t} from 'sentry/locale';
import HookStore from 'sentry/stores/hookStore';
import ProjectsStore from 'sentry/stores/projectsStore';
@@ -359,6 +362,31 @@ class ProjectFiltersSettings extends AsyncComponent {
);
})}
+
+
+
+
+