Description
When sending a message in an unencrypted room, Synapse returns a 400 error with the following message:
1 validation error for Mentions
Input should be a valid dictionary or instance of Mentions [type=model_type, input_value=immutabledict({}), input_type=immutabledict]
For further information visit https://errors.pydantic.dev/2.12/v/model_type
Element Web shows the user: "some of your messages could not be sent"
Root Cause
Element Web sends "m.mentions": {} in the request body of every message, even when no one is mentioned. Synapse internally represents event content as immutabledict. When Pydantic v2 tries to validate this immutabledict({}) against the Mentions model, it fails because immutabledict is not recognized as a dict subtype by Pydantic v2.
In encrypted rooms this does not occur, because the content is encrypted before sending and Synapse never sees m.mentions in plaintext.
Expected Behavior
Message is sent successfully.
Actual Behavior
Synapse returns 400 - 1 validation error for Mentions
Environment
| Component |
Version |
| Synapse |
1.147.1 |
| Element Web |
1.12.8 / 1.12.12 |
| Browser |
Chrome 144/146/147 (Windows & macOS), Edge 146 |
- Encrypted rooms: not affected
- Unencrypted rooms: affected
Steps to reproduce
- Set up Synapse 1.147.1 with Element Web 1.12.8 / 1.12.12
- Create an unencrypted room
- Send any message (e.g. "hi")
- Synapse returns 400, Element Web shows "some of your messages could not be sent"
Homeserver
another homeserver
Synapse Version
1.147.1
Installation Method
Docker (matrixdotorg/synapse)
Database
single postgresql, no, no
Workers
Single process
Platform
runs in kubernetes
Configuration
No response
Relevant log output
synapse.http.server - INFO - PUT - <XForwardedForRequest method='PUT' uri='/_matrix/client/v3/rooms/.../send/m.room.message/...' clientproto='HTTP/1.0' site='8008'>
SynapseError: 400 - 1 validation error for Mentions
Input should be a valid dictionary or instance of Mentions [type=model_type, input_value=immutabledict({}), input_type=immutabledict]
For further information visit https://errors.pydantic.dev/2.12/v/model_type
Anything else that would be useful to know?
Element Web sending "m.mentions": {} on every message is a known issue: element-hq/element-web#26345
The fix should be in Synapse: the Mentions Pydantic model should handle immutabledict input by converting it to dict before validation, e.g. using a model_validator with mode='before':
from pydantic import model_validator
class Mentions(BaseModel):
user_ids: List[str] = Field(default_factory=list)
room: bool = False
@model_validator(mode='before')
@classmethod
def coerce_immutabledict(cls, v: Any) -> Any:
if hasattr(v, 'items') and not isinstance(v, dict):
return dict(v)
return v
Description
When sending a message in an unencrypted room, Synapse returns a
400error with the following message:Element Web shows the user: "some of your messages could not be sent"
Root Cause
Element Web sends
"m.mentions": {}in the request body of every message, even when no one is mentioned. Synapse internally represents event content asimmutabledict. When Pydantic v2 tries to validate thisimmutabledict({})against theMentionsmodel, it fails becauseimmutabledictis not recognized as adictsubtype by Pydantic v2.In encrypted rooms this does not occur, because the content is encrypted before sending and Synapse never sees
m.mentionsin plaintext.Expected Behavior
Message is sent successfully.
Actual Behavior
Synapse returns
400 - 1 validation error for MentionsEnvironment
Steps to reproduce
Homeserver
another homeserver
Synapse Version
1.147.1
Installation Method
Docker (matrixdotorg/synapse)
Database
single postgresql, no, no
Workers
Single process
Platform
runs in kubernetes
Configuration
No response
Relevant log output
Anything else that would be useful to know?
Element Web sending
"m.mentions": {}on every message is a known issue: element-hq/element-web#26345The fix should be in Synapse: the
MentionsPydantic model should handleimmutabledictinput by converting it todictbefore validation, e.g. using amodel_validatorwithmode='before':