Return 400 for a syntactically invalid pos after all

Removing M_UNKNOWN_POS is about well-formed positions the server no
longer recognises; a malformed token is a plain client error like
everywhere else. Reverts part of 20feb1ef15 and keeps a test pinning
the 400.
This commit is contained in:
Matthew Hodgson
2026-08-07 13:47:22 +03:00
parent 20feb1ef15
commit a1e9948e3c
2 changed files with 17 additions and 16 deletions
+6 -13
View File
@@ -23,7 +23,6 @@ sync servlet; only the request parsing and the top-level response differ.
import logging
from typing import TYPE_CHECKING
from synapse.api.errors import SynapseError
from synapse.http.server import HttpServer
from synapse.http.servlet import (
parse_and_validate_json_object_from_request,
@@ -81,18 +80,12 @@ class MSC4525PaginatedSyncRestServlet(SlidingSyncRestServlet):
from_token = None
if from_token_string is not None:
try:
from_token = await SlidingSyncStreamToken.from_string(
self.store, from_token_string
)
except SynapseError:
# There is no client error path in this API: an unparsable
# `pos` is treated the same as an unrecognised one - as
# absent, so the connection starts afresh.
logger.info(
"Unparsable paginated sync pos for %s; starting the connection afresh",
user,
)
# A syntactically invalid pos is a plain 400, as elsewhere. Only a
# well-formed pos the server doesn't recognise restarts the
# connection (see the handler).
from_token = await SlidingSyncStreamToken.from_string(
self.store, from_token_string
)
body = parse_and_validate_json_object_from_request(
request, MSC4525PaginatedSyncBody
@@ -339,9 +339,17 @@ class MSC4525PaginatedSyncTestCase(unittest.HomeserverTestCase):
for room_id, room_response in response["rooms"].items():
self.assertTrue(room_response.get("initial"), room_id)
# An entirely unparsable pos is treated the same way, not a 400.
response = self._sync(body, pos="not a token at all")
self.assertEqual(set(response["rooms"].keys()), set(room_ids))
# A syntactically invalid pos, by contrast, is a plain 400: only
# well-formed-but-unrecognised positions restart the connection.
path = (
self.sync_endpoint
+ "?"
+ urllib.parse.urlencode({"timeout": "0", "pos": "not a token at all"})
)
channel = self.make_request(
method="POST", path=path, content=body, access_token=self.tok
)
self.assertEqual(channel.code, 400, channel.json_body)
def test_cold_start_backlog_not_starved_by_live_traffic(self) -> None:
"""Rooms never sent on the connection get a reserved slice of every