diff --git a/CHANGES.md b/CHANGES.md index bcf66142f9..c6e06aad49 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,12 @@ +# Synapse 1.151.0 (2026-04-07) + +## Bugfixes + +- Fix `KNOWN_ROOM_VERSIONS.__contains__` raising `TypeError` for non-string keys, which could cause `/sync` to fail for rooms with a `NULL` room version in the database. Bug introduced in [#19589](https://github.com/element-hq/synapse/pull/19589) as part of v1.151.0rc1. ([\#19649](https://github.com/element-hq/synapse/issues/19649)) + + + + # Synapse 1.151.0rc1 (2026-03-31) ## Features diff --git a/changelog.d/19642.feature b/changelog.d/19642.feature new file mode 100644 index 0000000000..dfb8e5f5a3 --- /dev/null +++ b/changelog.d/19642.feature @@ -0,0 +1 @@ +Advertise [MSC4445](https://github.com/matrix-org/matrix-spec-proposals/pull/4445) sync timeline order in `unstable_features`. diff --git a/changelog.d/19645.doc b/changelog.d/19645.doc new file mode 100644 index 0000000000..e46e048952 --- /dev/null +++ b/changelog.d/19645.doc @@ -0,0 +1 @@ +Fix Docker image link typo in worker docs. diff --git a/debian/changelog b/debian/changelog index 36fa0e5140..f3dc1eb0c2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +matrix-synapse-py3 (1.151.0) stable; urgency=medium + + * New synapse release 1.151.0. + + -- Synapse Packaging team Tue, 07 Apr 2026 12:15:10 +0000 + matrix-synapse-py3 (1.151.0~rc1) stable; urgency=medium * New synapse release 1.151.0rc1. diff --git a/docs/workers.md b/docs/workers.md index c2aef33e16..201ef8f854 100644 --- a/docs/workers.md +++ b/docs/workers.md @@ -60,10 +60,10 @@ virtualenv, these can be installed with: pip install "matrix-synapse[redis]" ``` -Note that these dependencies are included when synapse is installed with `pip -install matrix-synapse[all]`. They are also included in the debian packages from -`packages.matrix.org` and in the docker images at -https://hub.docker.com/r/ectorim/synapse/. +Note that these dependencies are included when Synapse is installed with `pip install +matrix-synapse[all]`. They are also included in the [Debian +packages](setup/installation.md#debianubuntu) and in the [Docker +images](setup/installation.md#docker-images-and-ansible-playbooks). To make effective use of the workers, you will need to configure an HTTP reverse-proxy such as nginx or haproxy, which will direct incoming requests to diff --git a/pyproject.toml b/pyproject.toml index 27cdbcb671..5fa7dade52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "matrix-synapse" -version = "1.151.0rc1" +version = "1.151.0" description = "Homeserver for the Matrix decentralised comms protocol" readme = "README.rst" authors = [ diff --git a/rust/src/room_versions.rs b/rust/src/room_versions.rs index c0b304e18c..fbcc32516a 100644 --- a/rust/src/room_versions.rs +++ b/rust/src/room_versions.rs @@ -600,7 +600,12 @@ impl KnownRoomVersionsMapping { Ok(()) } - fn __getitem__(&self, key: &str) -> PyResult { + fn __getitem__(&self, key: &Bound<'_, PyAny>) -> PyResult { + // We need to accept anything as the key, but we know that only strings + // are valid keys, so if it's not a string we just raise a KeyError. + let Ok(key) = key.extract::<&str>() else { + return Err(PyKeyError::new_err(key.to_string())); + }; let versions = self.versions.read().unwrap(); versions .iter() @@ -609,9 +614,14 @@ impl KnownRoomVersionsMapping { .ok_or_else(|| PyKeyError::new_err(key.to_string())) } - fn __contains__(&self, key: &str) -> PyResult { + fn __contains__(&self, key: &Bound<'_, PyAny>) -> bool { + // We need to accept anything as the key, but we know that only strings + // are valid keys, so if it's not a string we just return false. + let Ok(key) = key.extract::<&str>() else { + return false; + }; let versions = self.versions.read().unwrap(); - Ok(versions.iter().any(|v| v.identifier == key)) + versions.iter().any(|v| v.identifier == key) } fn keys(&self) -> PyResult> { diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py index 7bf4b12e8b..bb1711f2cf 100644 --- a/synapse/rest/client/versions.py +++ b/synapse/rest/client/versions.py @@ -206,6 +206,8 @@ class VersionsRestServlet(RestServlet): "org.matrix.msc4354": self.config.experimental.msc4354_enabled, # MSC4380: Invite blocking "org.matrix.msc4380.stable": True, + # MSC4445: Sync timeline order + "org.matrix.msc4445.initial_sync_timeline_topological_ordering": True, }, }, ) diff --git a/tests/synapse_rust/test_room_versions.py b/tests/synapse_rust/test_room_versions.py new file mode 100644 index 0000000000..aff1ab2783 --- /dev/null +++ b/tests/synapse_rust/test_room_versions.py @@ -0,0 +1,45 @@ +# +# 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: +# . + +from synapse.api.room_versions import KNOWN_ROOM_VERSIONS + +from tests import unittest + + +class KnownRoomVersionsMappingTestCase(unittest.TestCase): + """Tests for the Rust-backed KNOWN_ROOM_VERSIONS mapping.""" + + def test_contains_none(self) -> None: + """Test that `None in KNOWN_ROOM_VERSIONS` returns False + rather than raising a TypeError, matching Python dict semantics.""" + self.assertFalse(None in KNOWN_ROOM_VERSIONS) + + def test_contains_non_string(self) -> None: + """Test that non-string keys return False from __contains__.""" + self.assertFalse(42 in KNOWN_ROOM_VERSIONS) # type: ignore[comparison-overlap] + self.assertFalse(3.14 in KNOWN_ROOM_VERSIONS) # type: ignore[comparison-overlap] + self.assertFalse([] in KNOWN_ROOM_VERSIONS) # type: ignore[comparison-overlap] + + def test_contains_known_version(self) -> None: + self.assertTrue("1" in KNOWN_ROOM_VERSIONS) + + def test_contains_unknown_version(self) -> None: + self.assertFalse("unknown" in KNOWN_ROOM_VERSIONS) + + def test_getitem_non_string_raises_key_error(self) -> None: + """Test that KNOWN_ROOM_VERSIONS[non_string] raises KeyError, + not TypeError, matching Python dict semantics.""" + with self.assertRaises(KeyError): + KNOWN_ROOM_VERSIONS[42] # type: ignore[index] + with self.assertRaises(KeyError): + KNOWN_ROOM_VERSIONS[None] # type: ignore[index]