`_dispatch_sync` gathers the mautrix per-event handler tasks with a bare
`asyncio.gather(*tasks)`. Without `return_exceptions=True`, the first handler
that raises aborts the gather, so the sibling events in the same sync response
are dropped unprocessed — the exception propagates up to the sync loop, which
logs a single "sync error" and moves on. The invite/redaction gathers a few
lines above already use `return_exceptions=True`.
Use `return_exceptions=True` and log each failing handler, so one bad event no
longer takes out the rest of its batch and per-event failures stay visible.
Regression test: a batch with one failing and one succeeding handler no longer
raises, the good handler still runs, and the failure is logged (mutation-
verified — reverting re-raises RuntimeError out of _dispatch_sync).