From 21404ad3c5491e2a1b6bcced61a7f17304e00ff9 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Tue, 21 Jul 2026 16:31:31 -0500 Subject: [PATCH 1/4] Fix config regression around falsy `experimental_features` Fix https://github.com/element-hq/synapse/issues/19986 --- synapse/config/server.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/synapse/config/server.py b/synapse/config/server.py index 832183f32bb..16479170d0b 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -966,9 +966,10 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None: # The maximum number of delayed events a user may have scheduled at a time. # (Defined here despite being experimental to be near the other MSC4140 config) - self.max_delayed_events_per_user: int = config.get( - "experimental_features", {} - ).get("msc4140_max_delayed_events_per_user", 100) + experimental = config.get("experimental_features") or {} + self.max_delayed_events_per_user: int = experimental.get( + "msc4140_max_delayed_events_per_user", 100 + ) if ( not isinstance(self.max_delayed_events_per_user, int) or self.max_delayed_events_per_user < 0 From 56e9e821707ee35b80e1a6e1479326bfe80cf383 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Tue, 21 Jul 2026 17:10:44 -0500 Subject: [PATCH 2/4] Add tests --- tests/config/test_experimental.py | 71 +++++++++++++++++++++++++++++++ tests/config/test_server.py | 24 +++++++++++ 2 files changed, 95 insertions(+) create mode 100644 tests/config/test_experimental.py diff --git a/tests/config/test_experimental.py b/tests/config/test_experimental.py new file mode 100644 index 00000000000..0fede98ee9d --- /dev/null +++ b/tests/config/test_experimental.py @@ -0,0 +1,71 @@ +# +# This file is licensed under the Affero General Public License (AGPL) version 3. +# +# Copyright (C) 2026 Element Creations Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# See the GNU Affero General Public License for more details: +# . + + +import yaml +from parameterized import parameterized + +from synapse.config._base import RootConfig +from synapse.config.experimental import ExperimentalConfig +from synapse.config.homeserver import HomeServerConfig +from synapse.types import JsonDict + +from tests import unittest + + +class ExperimentalConfigTestCase(unittest.TestCase): + @parameterized.expand( + [ + [ + "single", + { + "experimental_features": { + "msc3575_enabled": True, + } + }, + ], + [ + "multi", + { + "experimental_features": { + "msc3575_enabled": True, + "msc3030_enabled": True, + } + }, + ], + # This has historically worked and this is being added as a regression test + ["none", {"experimental_features": None}], + ] + ) + def test_experimental_features_parsing( + self, test_description: str, config_values: JsonDict + ) -> None: + """ + Test the that `experimental_features` parses with these values + """ + + _read_config(config_values) + + +def _read_config(config_values: JsonDict) -> None: + ExperimentalConfig(RootConfig()).read_config( + yaml.safe_load( + HomeServerConfig().generate_config( + config_dir_path="CONFDIR", + data_dir_path="/data_dir_path", + server_name="che.org", + ) + ) + | config_values, + allow_secrets_in_config=False, + ) diff --git a/tests/config/test_server.py b/tests/config/test_server.py index 41ea8fb5b1b..f718e20a2a9 100644 --- a/tests/config/test_server.py +++ b/tests/config/test_server.py @@ -22,6 +22,7 @@ from typing import Any import yaml +from parameterized import parameterized from synapse.config._base import ConfigError, RootConfig from synapse.config.homeserver import HomeServerConfig @@ -228,6 +229,29 @@ def generate_config(value: Any) -> JsonDict: with self.assertRaises(ConfigError): _read_config(generate_config(disallowed_value)) + @parameterized.expand( + [ + [ + "single", + { + "experimental_features": { + "msc4140_max_delayed_events_per_user": 3, + } + }, + ], + # This has historically worked and this is being added as a regression test + ["none", {"experimental_features": None}], + ] + ) + def test_experimental_features_parsing( + self, test_description: str, config_values: JsonDict + ) -> None: + """ + Test the that `experimental_features` parses with these values + """ + + _read_config(config_values) + def _read_config(config_values: JsonDict) -> None: ServerConfig(RootConfig()).read_config( From 1c9e6e8d73f87a04977c1aad7662600ac7f41e8c Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Tue, 21 Jul 2026 19:26:53 -0500 Subject: [PATCH 3/4] Add changelog --- changelog.d/19987.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/19987.bugfix diff --git a/changelog.d/19987.bugfix b/changelog.d/19987.bugfix new file mode 100644 index 00000000000..396d863c1e5 --- /dev/null +++ b/changelog.d/19987.bugfix @@ -0,0 +1 @@ +Stub changelog: Merge with #19539. From 593d37bc7cbb13180a37f7038e2cba30bec1dc3a Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Wed, 22 Jul 2026 09:00:07 -0500 Subject: [PATCH 4/4] Actual changelog --- changelog.d/19987.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/19987.bugfix b/changelog.d/19987.bugfix index 396d863c1e5..73f97c1f7da 100644 --- a/changelog.d/19987.bugfix +++ b/changelog.d/19987.bugfix @@ -1 +1 @@ -Stub changelog: Merge with #19539. +Fix config regression around falsy `experimental_features` no longer being accepted.