Fix the /profile endpoint giving 500 when the profile does not exist at all. (#20149)

Drive-by fix for a problem noticed when doing some other work.

This only triggers for 'custom'/generic fields, not the built-in
`displayname` and `avatar_url`.

Not seeing an open issue for it.

Return a 404, not 500, when looking up a profile field on a missing
profile

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
This commit is contained in:
Olivier 'reivilibre
2026-08-26 14:27:28 +01:00
committed by GitHub
parent d27933f121
commit 781d08df26
3 changed files with 30 additions and 4 deletions
+1
View File
@@ -0,0 +1 @@
Fix the `/profile` endpoint, when querying custom fields, returning a 500 error instead of 404 when the profile does not exist at all.
+12 -4
View File
@@ -291,11 +291,14 @@ class ProfileWorkerStore(SQLBaseStore):
(field_path, field_name, user_id.localpart),
)
row = txn.fetchone()
if row is None:
# The user has no profile at all.
raise StoreError(404, "No row found")
# Test exists first since value being None is used for both
# missing and a null JSON value.
exists, value = cast(
tuple[bool, JsonValue | dict[str, JsonValue]], txn.fetchone()
)
exists, value = cast(tuple[bool, JsonValue | dict[str, JsonValue]], row)
if not exists:
raise StoreError(404, "No row found")
return value
@@ -310,9 +313,14 @@ class ProfileWorkerStore(SQLBaseStore):
(field_path, field_path, user_id.localpart),
)
row = txn.fetchone()
if row is None:
# The user has no profile at all.
raise StoreError(404, "No row found")
# If value_type is None, then the value did not exist.
value_type, value = cast(
tuple[str | None, JsonValue | dict[str, JsonValue]], txn.fetchone()
tuple[str | None, JsonValue | dict[str, JsonValue]], row
)
if not value_type:
raise StoreError(404, "No row found")
+17
View File
@@ -19,9 +19,12 @@
#
#
from http import HTTPStatus
from twisted.internet.testing import MemoryReactor
from synapse.api.constants import ProfileFields
from synapse.api.errors import StoreError
from synapse.server import HomeServer
from synapse.storage.database import LoggingTransaction
from synapse.storage.engines import PostgresEngine
@@ -95,6 +98,20 @@ class ProfileStoreTestCase(unittest.HomeserverTestCase):
self.get_success(self.store.get_profile_avatar_url(self.u_frank))
)
def test_get_profile_field_without_profile(self) -> None:
"""
Getting a custom profile field for a user that has no row in the
`profiles` table at all should raise a 404.
Regression test (we previously would trigger an unhandled exception).
Can happen for users whose profile was erased upon deactivation.
"""
f = self.get_failure(
self.store.get_profile_field(self.u_frank, "org.example.field"),
StoreError,
)
self.assertEqual(f.value.code, HTTPStatus.NOT_FOUND)
def test_profiles_bg_migration(self) -> None:
"""
Test background job that copies entries from column user_id to full_user_id, adding