diff --git a/extensions/matrix/src/matrix/sdk.test.ts b/extensions/matrix/src/matrix/sdk.test.ts index 2faa518f472..d9f869551ff 100644 --- a/extensions/matrix/src/matrix/sdk.test.ts +++ b/extensions/matrix/src/matrix/sdk.test.ts @@ -1050,6 +1050,7 @@ describe("MatrixClient crypto bootstrapping", () => { expect(bootstrapSpy).toHaveBeenCalledTimes(2); expect((bootstrapSpy.mock.calls as unknown[][])[1]?.[1] ?? {}).toEqual({ forceResetCrossSigning: true, + allowSecretStorageRecreateWithoutRecoveryKey: true, strict: true, }); }); diff --git a/extensions/matrix/src/matrix/sdk.ts b/extensions/matrix/src/matrix/sdk.ts index 6a4c365283c..314b675c027 100644 --- a/extensions/matrix/src/matrix/sdk.ts +++ b/extensions/matrix/src/matrix/sdk.ts @@ -465,8 +465,13 @@ export class MatrixClient { ); } else if (this.password?.trim()) { try { + // The repair path already force-resets cross-signing; allow secret storage + // recreation so the new keys can be persisted. Without this, a device that + // lost its recovery key enters a permanent failure loop because the new + // cross-signing keys have nowhere to be stored. const repaired = await cryptoBootstrapper.bootstrap(crypto, { forceResetCrossSigning: true, + allowSecretStorageRecreateWithoutRecoveryKey: true, strict: true, }); if (repaired.crossSigningPublished && repaired.ownDeviceVerified !== false) {