Skip to main content

xmtp_proto/
error.rs

1use std::array::TryFromSliceError;
2
3use thiserror::Error;
4use xmtp_common::{ErrorCode, RetryableError};
5
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub enum ApiEndpoint {
8    Publish,
9    SubscribeGroupMessages,
10    SubscribeWelcomes,
11    UploadKeyPackage,
12    FetchKeyPackages,
13    SendGroupMessages,
14    SendWelcomeMessages,
15    QueryGroupMessages,
16    QueryWelcomeMessages,
17    PublishIdentityUpdate,
18    GetInboxIds,
19    GetIdentityUpdatesV2,
20    VerifyScwSignature,
21    QueryV4Envelopes,
22    PublishEnvelopes,
23    PublishCommitLog,
24    QueryCommitLog,
25    HealthCheck,
26    GetNodes,
27    Path(String),
28    GetNewestGroupMessage,
29}
30
31impl std::fmt::Display for ApiEndpoint {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
33        use ApiEndpoint::*;
34        match self {
35            Publish => write!(f, "publish"),
36            SubscribeGroupMessages => write!(f, "subscribe_group_messages"),
37            SubscribeWelcomes => write!(f, "subscribe_welcomes"),
38            UploadKeyPackage => write!(f, "upload_key_package"),
39            FetchKeyPackages => write!(f, "fetch_key_packages"),
40            SendGroupMessages => write!(f, "send_group_messages"),
41            SendWelcomeMessages => write!(f, "send_welcome_messages"),
42            QueryGroupMessages => write!(f, "query_group_messages"),
43            QueryWelcomeMessages => write!(f, "query_welcome_messages"),
44            PublishIdentityUpdate => write!(f, "publish_identity_update"),
45            GetInboxIds => write!(f, "get_inbox_ids"),
46            GetIdentityUpdatesV2 => write!(f, "get_identity_updates_v2"),
47            VerifyScwSignature => write!(f, "verify_scw_signature"),
48            QueryV4Envelopes => write!(f, "query_v4_envelopes"),
49            PublishEnvelopes => write!(f, "publish_envelopes"),
50            PublishCommitLog => write!(f, "publish_commit_log"),
51            QueryCommitLog => write!(f, "query_commit_log"),
52            HealthCheck => write!(f, "health_check"),
53            GetNodes => write!(f, "get_nodes"),
54            Path(s) => write!(f, "{}", s),
55            GetNewestGroupMessage => write!(f, "get_newest_group_message"),
56        }
57    }
58}
59
60/// General Error types for use when converting/deserializing From/To Protos
61/// Loosely Modeled after serdes [error](https://docs.rs/serde/latest/serde/de/value/struct.Error.html) type.
62/// This general error type avoid circular hard-dependencies on crates further up the tree
63/// (xmtp_id/xmtp_mls) if they had defined the error themselves.
64#[derive(thiserror::Error, Debug, ErrorCode)]
65pub enum ConversionError {
66    /// Missing field.
67    ///
68    /// Required field missing during proto conversion. Not retryable.
69    #[error("missing field {} of type {} during conversion from protobuf", .item, .r#type)]
70    Missing {
71        /// the item being converted
72        item: &'static str,
73        /// type of the item being converted
74        r#type: &'static str,
75    },
76    /// Unspecified field.
77    ///
78    /// Protobuf field is unspecified. Not retryable.
79    #[error("field {} unspecified", _0)]
80    Unspecified(&'static str),
81    /// Deprecated field.
82    ///
83    /// A deprecated protobuf field was used. Not retryable.
84    #[error("field {} deprecated", _0)]
85    Deprecated(&'static str),
86    /// Invalid length.
87    ///
88    /// Data has wrong length for conversion. Not retryable.
89    #[error("type {} has invalid length. expected {} got {}", .item, .expected, .got)]
90    InvalidLength {
91        /// the item being converted
92        item: &'static str,
93        /// expected length of the item being converted
94        expected: usize,
95        /// the length of the received item
96        got: usize,
97    },
98    /// Invalid value.
99    ///
100    /// Data has unexpected value. Not retryable.
101    #[error("type {} invalid. expected {}, got {}", .item, .expected, .got)]
102    InvalidValue {
103        /// the item being converted
104        item: &'static str,
105        /// description of the item expected, i.e 'a negative integer'
106        expected: &'static str,
107        /// description of the value received i.e 'a positive integer'
108        got: String,
109    },
110    /// Decode error.
111    ///
112    /// Protobuf decoding failed. Not retryable.
113    #[error("decoding proto {0}")]
114    Decode(#[from] prost::DecodeError),
115    /// Encode error.
116    ///
117    /// Protobuf encoding failed. Not retryable.
118    #[error("encoding proto {0}")]
119    Encode(#[from] prost::EncodeError),
120    /// Unknown enum value.
121    ///
122    /// Protobuf enum has unrecognized value. Not retryable.
123    #[error("Unknown enum value {0}")]
124    UnknownEnumValue(#[from] prost::UnknownEnumValue),
125    /// Ed25519 signature error.
126    ///
127    /// Ed25519 signature bytes invalid. Not retryable.
128    // we keep Ed signature bytes on ProtoBuf definitions
129    #[error(transparent)]
130    EdSignature(#[from] ed25519_dalek::ed25519::Error),
131
132    /// Invalid public key.
133    ///
134    /// Public key validation failed. Not retryable.
135    #[error("{} is invalid: {:?}", .description, .value)]
136    InvalidPublicKey {
137        // What kind of key is invalid
138        description: &'static str,
139        // What is the value
140        value: Option<String>,
141    },
142    /// Invalid version.
143    ///
144    /// Protocol version not supported. Not retryable.
145    #[error("version not supported")]
146    InvalidVersion,
147    /// OpenMLS error.
148    ///
149    /// OpenMLS library error. Not retryable.
150    // TODO: Probably should not be apart of conversion,
151    // conversions using openml sshould be put further up the stack
152    #[error(transparent)]
153    OpenMls(#[from] openmls::prelude::Error),
154    /// Protocol message error.
155    ///
156    /// MLS protocol message error. Not retryable.
157    #[error(transparent)]
158    Protocol(#[from] openmls::framing::errors::ProtocolMessageError),
159    /// Builder error.
160    ///
161    /// Builder field not initialized. Not retryable.
162    #[error(transparent)]
163    Builder(#[from] derive_builder::UninitializedFieldError),
164    /// Slice error.
165    ///
166    /// Byte slice conversion failed. Not retryable.
167    #[error(transparent)]
168    Slice(#[from] TryFromSliceError),
169}
170
171// Conversion errors themselves not really retryable because the bytes are static,
172// the conversions are done in-memory, so a retrying a conversion should not change the outcome.
173// The API call is what should be retried.
174// If retry on a conversion error is desired a new error enum + custom Retrayble implementation
175// should be preferred.
176impl RetryableError for ConversionError {
177    fn is_retryable(&self) -> bool {
178        false
179    }
180}
181
182/// Error resulting from proto conversions/mutations
183#[derive(Debug, Error, ErrorCode)]
184pub enum ProtoError {
185    /// Hex error.
186    ///
187    /// Hex encoding/decoding failed. Not retryable.
188    #[error(transparent)]
189    Hex(#[from] hex::FromHexError),
190    /// Decode error.
191    ///
192    /// Protobuf decoding failed. Not retryable.
193    #[error(transparent)]
194    Decode(#[from] prost::DecodeError),
195    /// Encode error.
196    ///
197    /// Protobuf encoding failed. Not retryable.
198    #[error(transparent)]
199    Encode(#[from] prost::EncodeError),
200    /// OpenMLS error.
201    ///
202    /// OpenMLS library error. Not retryable.
203    #[error("Open MLS {0}")]
204    OpenMls(#[from] openmls::prelude::Error),
205    /// MLS protocol message error.
206    ///
207    /// MLS framing error. Not retryable.
208    #[error(transparent)]
209    MlsProtocolMessage(#[from] openmls::framing::errors::ProtocolMessageError),
210    /// Key package error.
211    ///
212    /// Key package verification failed. Not retryable.
213    #[error(transparent)]
214    KeyPackage(#[from] openmls::prelude::KeyPackageVerifyError),
215    /// Not found.
216    ///
217    /// Proto resource not found. Not retryable.
218    #[error("{0} not found")]
219    NotFound(String),
220}