fix(matrix): device_id fallback prevents E2EE init failure on fresh bot accounts

- Resolve device_id via query_keys({mxid: []}) when whoami() returns None
- Guard _verify_device_keys_on_server and _reverify_keys_after_upload
  against None/unverified device_id to prevent 'device_keys values must
  be a list of strings' serialization failure
- Disconnect existing client before reconnect to prevent dual OlmMachine
  instances on the same crypto store

Re-targeted from #39779 (legacy gateway/platforms/matrix.py) onto the
migrated plugins/platforms/matrix/adapter.py path following the
2026-06-20 adapter migration. Logic unchanged from original fix.

242 tests passing (233 upstream + 9 new).
This commit is contained in:
Gary Walker 2026-06-28 15:02:04 +10:00 committed by kshitij
parent 2d8d08cae6
commit 9048457eab
2 changed files with 440 additions and 0 deletions

View file

@ -806,6 +806,7 @@ class MatrixAdapter(BasePlatformAdapter):
self._device_id: str = config.extra.get("device_id", "") or os.getenv(
"MATRIX_DEVICE_ID", ""
)
self._device_id_unverified: bool = False
self._client: Any = None # mautrix.client.Client
self._crypto_db: Any = None # mautrix.util.async_db.Database
@ -1039,6 +1040,12 @@ class MatrixAdapter(BasePlatformAdapter):
self, client: Any, local_ed25519: str
) -> bool:
"""Re-query the server after share_keys() and verify our ed25519 key matches."""
if not client.device_id or self._device_id_unverified:
logger.warning(
"Matrix: skipping post-upload key verification — "
"device_id not yet established"
)
return True
try:
resp = await client.query_keys({client.mxid: [client.device_id]})
dk = getattr(resp, "device_keys", {}) or {}
@ -1065,6 +1072,12 @@ class MatrixAdapter(BasePlatformAdapter):
Returns True if keys are valid or were successfully re-uploaded.
Returns False if verification fails (caller should refuse E2EE).
"""
if not client.device_id or self._device_id_unverified:
logger.warning(
"Matrix: skipping device key verification — "
"device_id not yet established"
)
return True
try:
resp = await client.query_keys({client.mxid: [client.device_id]})
except Exception as exc:
@ -1139,6 +1152,12 @@ class MatrixAdapter(BasePlatformAdapter):
async def connect(self, *, is_reconnect: bool = False) -> bool:
"""Connect to the Matrix homeserver and start syncing."""
if self._client is not None:
try:
await self.disconnect()
except Exception as exc:
logger.warning("Matrix: error disconnecting before reconnect: %s", exc)
from mautrix.api import HTTPAPI
from mautrix.client import Client
from mautrix.client.state_store import MemoryStateStore, MemorySyncStore
@ -1189,6 +1208,36 @@ class MatrixAdapter(BasePlatformAdapter):
if effective_device_id:
client.device_id = effective_device_id
if not client.device_id:
try:
dev_resp = await client.query_keys({client.mxid: []})
all_devices = (
(getattr(dev_resp, "device_keys", {}) or {})
.get(str(client.mxid)) or {}
)
if len(all_devices) == 1:
client.device_id = next(iter(all_devices))
elif len(all_devices) == 0:
logger.warning(
"Matrix: no devices found for %s"
"key verification will be skipped",
client.mxid,
)
except Exception as exc:
logger.warning(
"Matrix: device list query failed: %s", exc
)
if not client.device_id:
logger.warning(
"Matrix: device_id could not be resolved for %s. "
"Set MATRIX_DEVICE_ID for full key verification. "
"E2EE will proceed without server-side device "
"key confirmation.",
client.mxid,
)
self._device_id_unverified = True
logger.info(
"Matrix: using access token for %s%s",
self._user_id or "(unknown user)",