From 70665a84aa995f6569f6ebd760edc1941d0f35b0 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 11 Feb 2026 14:11:51 +0100 Subject: [PATCH] feat(client): make it possible to subscribe to key upload errors (#6135) Signed-off-by: Johannes Marbach --- bindings/matrix-sdk-ffi/CHANGELOG.md | 3 ++ bindings/matrix-sdk-ffi/src/client.rs | 48 +++++++++++++++++++++++++ crates/matrix-sdk/CHANGELOG.md | 3 ++ crates/matrix-sdk/src/client/mod.rs | 22 +++++++++++- crates/matrix-sdk/src/encryption/mod.rs | 26 ++++++++++---- 5 files changed, 94 insertions(+), 8 deletions(-) diff --git a/bindings/matrix-sdk-ffi/CHANGELOG.md b/bindings/matrix-sdk-ffi/CHANGELOG.md index 1871b7331..4dcab832b 100644 --- a/bindings/matrix-sdk-ffi/CHANGELOG.md +++ b/bindings/matrix-sdk-ffi/CHANGELOG.md @@ -31,6 +31,9 @@ All notable changes to this project will be documented in this file. ### Features +- Add `Client::subscribe_to_duplicate_key_upload_errors` for listening to duplicate key + upload errors from `/keys/upload`. + ([#6135](https://github.com/matrix-org/matrix-rust-sdk/pull/6135/)) - Add `NotificationItem::raw_event` to get the raw event content of the event that triggered the notification, which can be useful for debugging and to support clients that want to implement custom handling for certain notifications. ([#6122](https://github.com/matrix-org/matrix-rust-sdk/pull/6122)) - [**breaking**] Extend `TimelineFocus::Event` to allow marking the target event as the root of a thread. diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index d7b41122c..eb4f09653 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -237,6 +237,32 @@ pub trait AccountDataListener: SyncOutsideWasm + SendOutsideWasm { fn on_change(&self, event: AccountDataEvent); } +/// A listener for duplicate key upload errors triggered by requests to +/// /keys/upload. +#[matrix_sdk_ffi_macros::export(callback_interface)] +pub trait DuplicateKeyUploadErrorListener: SyncOutsideWasm + SendOutsideWasm { + /// Called once when uploading keys fails. + fn on_duplicate_key_upload_error(&self, message: Option); +} + +/// Information about the old and new key that caused a duplicate key upload +/// error in /keys/upload. +#[derive(uniffi::Record)] +pub struct DuplicateOneTimeKeyErrorMessage { + /// The previously uploaded one-time key, encoded as unpadded base64. + pub old_key: String, + /// The one-time key we attempted to upload, encoded as unpadded base64 + pub new_key: String, +} + +impl From + for DuplicateOneTimeKeyErrorMessage +{ + fn from(value: matrix_sdk::encryption::DuplicateOneTimeKeyErrorMessage) -> Self { + Self { old_key: value.old_key.to_base64(), new_key: value.new_key.to_base64() } + } +} + /// A listener for changes of room account data events. #[matrix_sdk_ffi_macros::export(callback_interface)] pub trait RoomAccountDataListener: SyncOutsideWasm + SendOutsideWasm { @@ -748,6 +774,28 @@ impl Client { }))) } + /// Subscribe to duplicate key upload errors triggered by requests to + /// /keys/upload. + pub fn subscribe_to_duplicate_key_upload_errors( + &self, + listener: Box, + ) -> Arc { + let mut subscriber = self.inner.subscribe_to_duplicate_key_upload_errors(); + + Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move { + loop { + match subscriber.recv().await { + Ok(message) => { + listener.on_duplicate_key_upload_error(message.map(|m| m.into())) + } + Err(err) => { + error!("error when listening to key upload errors: {err}"); + } + } + } + }))) + } + /// Subscribe to updates of global account data events. /// /// Be careful that only the most recent value can be observed. Subscribers diff --git a/crates/matrix-sdk/CHANGELOG.md b/crates/matrix-sdk/CHANGELOG.md index 3018e31e5..a5382ad20 100644 --- a/crates/matrix-sdk/CHANGELOG.md +++ b/crates/matrix-sdk/CHANGELOG.md @@ -8,6 +8,9 @@ All notable changes to this project will be documented in this file. ### Features +- Add `Client::subscribe_to_duplicate_key_upload_errors` for listening to duplicate key + upload errors from `/keys/upload`. + ([#6135](https://github.com/matrix-org/matrix-rust-sdk/pull/6135/)) - Add `Room::pin_event` and `Room::unpin_event`, which allow pinning and unpinning events from a room. These were extracted from the `matrix_sdk_ui` crate, with no changes in functionality. ([#6106](https://github.com/matrix-org/matrix-rust-sdk/pull/6106)) diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index 52e084e85..6cc9943a3 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -119,7 +119,10 @@ use crate::{ #[cfg(feature = "e2e-encryption")] use crate::{ cross_process_lock::CrossProcessLock, - encryption::{Encryption, EncryptionData, EncryptionSettings, VerificationState}, + encryption::{ + DuplicateOneTimeKeyErrorMessage, Encryption, EncryptionData, EncryptionSettings, + VerificationState, + }, }; mod builder; @@ -387,6 +390,12 @@ pub(crate) struct ClientInner { /// A monitor for background tasks spawned by the client. pub(crate) task_monitor: TaskMonitor, + + /// A sender to notify subscribers about duplicate key upload errors + /// triggered by requests to /keys/upload. + #[cfg(feature = "e2e-encryption")] + pub(crate) duplicate_key_upload_error_sender: + broadcast::Sender>, } impl ClientInner { @@ -454,6 +463,8 @@ impl ClientInner { search_index: search_index_handler, thread_subscription_catchup, task_monitor: TaskMonitor::new(), + #[cfg(feature = "e2e-encryption")] + duplicate_key_upload_error_sender: broadcast::channel(1).0, }; #[allow(clippy::let_and_return)] @@ -3320,6 +3331,15 @@ impl Client { pub fn task_monitor(&self) -> &TaskMonitor { &self.inner.task_monitor } + + /// Add a subscriber for duplicate key upload error notifications triggered + /// by requests to /keys/upload. + #[cfg(feature = "e2e-encryption")] + pub fn subscribe_to_duplicate_key_upload_errors( + &self, + ) -> broadcast::Receiver> { + self.inner.duplicate_key_upload_error_sender.subscribe() + } } /// Contains the disk size of the different stores, if known. It won't be diff --git a/crates/matrix-sdk/src/encryption/mod.rs b/crates/matrix-sdk/src/encryption/mod.rs index d3e7dadd6..959183823 100644 --- a/crates/matrix-sdk/src/encryption/mod.rs +++ b/crates/matrix-sdk/src/encryption/mod.rs @@ -368,12 +368,12 @@ impl OAuthCrossSigningResetInfo { /// A struct that helps to parse the custom error message Synapse posts if a /// duplicate one-time key is uploaded. -#[derive(Debug)] -struct DuplicateOneTimeKeyErrorMessage { +#[derive(Clone, Debug)] +pub struct DuplicateOneTimeKeyErrorMessage { /// The previously uploaded one-time key. - old_key: Curve25519PublicKey, + pub old_key: Curve25519PublicKey, /// The one-time key we're attempting to upload right now. - new_key: Curve25519PublicKey, + pub new_key: Curve25519PublicKey, } impl FromStr for DuplicateOneTimeKeyErrorMessage { @@ -678,9 +678,10 @@ impl Client { .is_some(); if message.starts_with("One time key") && !already_reported { - if let Ok(message) = - DuplicateOneTimeKeyErrorMessage::from_str(message) - { + let error_message = + DuplicateOneTimeKeyErrorMessage::from_str(message); + + if let Ok(message) = &error_message { error!( sentry = true, old_key = %message.old_key, @@ -700,6 +701,17 @@ impl Client { StateStoreDataValue::OneTimeKeyAlreadyUploaded, ) .await?; + + if let Err(e) = self + .inner + .duplicate_key_upload_error_sender + .send(error_message.ok()) + { + error!( + "Failed to dispatch duplicate key upload error notification: {}", + e + ); + } } } }