# NousResearch/hermes-agent · Matrix inbound sync handler awaiting and dispatch issue

## Open now

- Nothing open on GitHub.

Two items share the title "fix(matrix): await inbound sync handlers", and a third is a bug report about inbound message dispatch on a Tuwunel homeserver after a mautrix migration (PR #18505). The titles suggest a shared Matrix inbound handling theme, but the available information does not confirm that the fixes address the reported bug.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #56345. Verify it addresses your case.
- **Recommended:** read PR #56345 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-56345`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #46142: \[Bug\]: Matrix gateway — mautrix migration (PR #18505) breaks inbound message dispatch on Tuwunel homeserver

## Prior & competing PRs

- **#56345: fix(matrix): await inbound sync handlers**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `plugins/platforms/matrix/adapter.py`, `scripts/release.py`, `tests/gateway/test_matrix.py`
  - fix hunks:
  - `plugins/platforms/matrix/adapter.py`

    ```diff
    @@ -1459,9 +1459,21 @@ async def connect(self, *, is_reconnect: bool = False) -> bool:
             # Without this the INVITE handler below never fires.
             client.add_dispatcher(MembershipEventDispatcher)
     
    -        client.add_event_handler(EventType.ROOM_MESSAGE, self._on_room_message)
    -        client.add_event_handler(EventType.REACTION, self._on_reaction)
    -        client.add_event_handler(IntEvt.INVITE, self._on_invite)
    +        client.add_event_handler(
    +            EventType.ROOM_MESSAGE,
    +            self._on_room_message,
    +            wait_sync=True,
    +        )
    +        client.add_event_handler(
    +            EventType.REACTION,
    +            self._on_reaction,
    +            wait_sync=True,
    +        )
    +        client.add_event_handler(
    +            IntEvt.INVITE,
    +            self._on_invite,
    +            wait_sync=True,
    +        )
     
             # Initial sync to catch up, then start background sync.
             self._startup_ts = time.time()
    ```
  - `scripts/release.py`

    ```diff
    @@ -232,6 +232,7 @@
         "290859878+synapsesx@users.noreply.github.com": "synapsesx",
         "157689911+itsflownium@users.noreply.github.com": "itsflownium",
         "dirtyren@users.noreply.github.com": "dirtyren",
    +    "153708448+hunjaiboy@users.noreply.github.com": "yyzquwu",  # PR #47567 salvage (Matrix: register inbound handlers with wait_sync=True so _dispatch_sync's gather awaits them; without it mautrix fire-and-forgets and inbound intake has no completion point)
         "[redacted:email]": "tgmerritt",  # PR #43553 salvage (parse vLLM's token-based output-cap error format so over-cap max_tokens 400s reduce the output cap instead of death-looping into compression)
         "13277570+justin-cyhuang@users.noreply.github.com": "justin-cyhuang",
         "[redacted:email]": "Tranquil-Flow",
    ```
  - `tests/gateway/test_matrix.py`

    ```diff
    @@ -106,7 +106,7 @@ def __init__(self, mxid=None, device_id=None, api=None,
                 self.crypto = None
                 self._event_handlers = {}
     
    -        def add_event_handler(self, event_type, handler):
    +        def add_event_handler(self, event_type, handler, **kwargs):
                 self._event_handlers.setdefault(event_type, []).append(handler)
     
             def add_dispatcher(self, dispatcher_type):
    ```

    ```diff
    @@ -2727,13 +2727,20 @@ async def test_connect_registers_encrypted_event_handler_when_encryption_on(self
                         with patch.object(adapter, "_sync_loop", AsyncMock(return_value=None)):
                             assert await adapter.connect() is True
     
    -        # Verify event handlers were registered.
    -        # In mautrix the order is: add_event_handler(EventType, callback)
    +        # Verify inbound event handlers were registered as sync-awaited
    +        # callbacks. mautrix only returns waited handler tasks from
    +        # handle_sync(), so background-only handlers leave _dispatch_sync()
    +        # without a completion point for Hermes' Matrix intake.
             handler_calls = mock_client.add_event_handler.call_args_list
    -        registered_types = [call.args[0] for call in handler_calls]
    +        waited_types = {
    +            str(call.args[0])
    +            for call in handler_calls
    +            if call.kwargs.get("wait_sync") is True
    +        }
     
    -        # Should have registered handlers for ROOM_MESSAGE, REACTION, INVITE
    -        assert len(handler_calls) >= 3
    +        assert "m.room.message" in waited_types
    +        assert "m.reaction" in waited_types
    +        assert "internal.invite" in waited_types
     
             await adapter.disconnect()
     
    ```
- **#47567: fix(matrix): await inbound sync handlers**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `4c2c54c78`, `#56345`

## Fix status

- Merged to main in PR #56345 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-07-01`.
- Subsystem: `gateway`
