Skip to main content

ruma_common/api/error/
kind.rs

1use std::str::FromStr;
2
3use as_variant::as_variant;
4use ruma_common::{
5    RoomVersionId,
6    api::error::{HeaderDeserializationError, HeaderSerializationError},
7    http_headers::{http_date_to_system_time, system_time_to_http_date},
8    serde::{JsonObject, StringEnum},
9};
10use web_time::{Duration, SystemTime};
11
12#[cfg(feature = "unstable-msc4406")]
13use crate::OwnedUserId;
14use crate::PrivOwnedStr;
15
16/// An enum for the error kind.
17///
18/// Items may contain additional information.
19#[derive(Clone, Debug, PartialEq, Eq)]
20#[non_exhaustive]
21// Please keep the variants sorted alphabetically.
22pub enum ErrorKind {
23    /// `M_APPSERVICE_LOGIN_UNSUPPORTED`
24    ///
25    /// An application service used the [`m.login.application_service`] type an endpoint from the
26    /// [legacy authentication API] in a way that is not supported by the homeserver, because the
27    /// server only supports the [OAuth 2.0 API].
28    ///
29    /// [`m.login.application_service`]: https://spec.matrix.org/v1.19/application-service-api/#server-admin-style-permissions
30    /// [legacy authentication API]: https://spec.matrix.org/v1.19/client-server-api/#legacy-api
31    /// [OAuth 2.0 API]: https://spec.matrix.org/v1.19/client-server-api/#oauth-20-api
32    AppserviceLoginUnsupported,
33
34    /// `M_BAD_ALIAS`
35    ///
36    /// One or more [room aliases] within the `m.room.canonical_alias` event do not point to the
37    /// room ID for which the state event is to be sent to.
38    ///
39    /// [room aliases]: https://spec.matrix.org/v1.19/client-server-api/#room-aliases
40    BadAlias,
41
42    /// `M_BAD_JSON`
43    ///
44    /// The request contained valid JSON, but it was malformed in some way, e.g. missing required
45    /// keys, invalid values for keys.
46    BadJson,
47
48    /// `M_BAD_STATE`
49    ///
50    /// The state change requested cannot be performed, such as attempting to unban a user who is
51    /// not banned.
52    BadState,
53
54    /// `M_BAD_STATUS`
55    ///
56    /// The application service returned a bad status.
57    BadStatus(BadStatusErrorData),
58
59    /// `M_CANNOT_LEAVE_SERVER_NOTICE_ROOM`
60    ///
61    /// The user is unable to reject an invite to join the [server notices] room.
62    ///
63    /// [server notices]: https://spec.matrix.org/v1.19/client-server-api/#server-notices
64    CannotLeaveServerNoticeRoom,
65
66    /// `M_CANNOT_OVERWRITE_MEDIA`
67    ///
68    /// The `PUT /_matrix/media/*/upload/{serverName}/{mediaId}` endpoint was called with a media ID
69    /// that already has content.
70    CannotOverwriteMedia,
71
72    /// `M_CAPTCHA_INVALID`
73    ///
74    /// The Captcha provided did not match what was expected.
75    CaptchaInvalid,
76
77    /// `M_CAPTCHA_NEEDED`
78    ///
79    /// A Captcha is required to complete the request.
80    CaptchaNeeded,
81
82    /// `M_CONCURRENT_WRITE`
83    ///
84    /// The sequence token provided when updating a rendezvous session
85    /// does not match the current sequence token.
86    #[cfg(feature = "unstable-msc4388")]
87    ConcurrentWrite,
88
89    /// `M_CONFLICTING_UNSUBSCRIPTION`
90    ///
91    /// Part of [MSC4306]: an automatic thread subscription has been skipped by the server, because
92    /// the user unsubsubscribed after the indicated subscribed-to event.
93    ///
94    /// [MSC4306]: https://github.com/matrix-org/matrix-spec-proposals/pull/4306
95    #[cfg(feature = "unstable-msc4306")]
96    ConflictingUnsubscription,
97
98    /// `M_CONNECTION_FAILED`
99    ///
100    /// The connection to the application service failed.
101    ConnectionFailed,
102
103    /// `M_CONNECTION_TIMEOUT`
104    ///
105    /// The connection to the application service timed out.
106    ConnectionTimeout,
107
108    /// `M_DUPLICATE_ANNOTATION`
109    ///
110    /// The request is an attempt to send a [duplicate annotation].
111    ///
112    /// [duplicate annotation]: https://spec.matrix.org/v1.19/client-server-api/#avoiding-duplicate-annotations
113    DuplicateAnnotation,
114
115    /// `M_EXCLUSIVE`
116    ///
117    /// The resource being requested is reserved by an application service, or the application
118    /// service making the request has not created the resource.
119    Exclusive,
120
121    /// `M_FORBIDDEN`
122    ///
123    /// Forbidden access, e.g. joining a room without permission, failed login.
124    Forbidden,
125
126    /// `M_GUEST_ACCESS_FORBIDDEN`
127    ///
128    /// The room or resource does not permit [guests] to access it.
129    ///
130    /// [guests]: https://spec.matrix.org/v1.19/client-server-api/#guest-access
131    GuestAccessForbidden,
132
133    /// `M_INCOMPATIBLE_ROOM_VERSION`
134    ///
135    /// The client attempted to join a room that has a version the server does not support.
136    IncompatibleRoomVersion(IncompatibleRoomVersionErrorData),
137
138    /// `M_INVALID_PARAM`
139    ///
140    /// A parameter that was specified has the wrong value. For example, the server expected an
141    /// integer and instead received a string.
142    InvalidParam,
143
144    /// `M_INVALID_ROOM_STATE`
145    ///
146    /// The initial state implied by the parameters to the `POST /_matrix/client/*/createRoom`
147    /// request is invalid, e.g. the user's `power_level` is set below that necessary to set the
148    /// room name.
149    InvalidRoomState,
150
151    /// `M_INVALID_USERNAME`
152    ///
153    /// The desired user name is not valid.
154    InvalidUsername,
155
156    /// `M_INVITE_BLOCKED`
157    ///
158    /// The invite was interdicted by moderation tools or configured access controls without having
159    /// been witnessed by the invitee.
160    InviteBlocked,
161
162    /// `M_KEY_TOO_LARGE`
163    ///
164    /// The [profile] key in the request exceeds the maximum allowed length of 255 bytes.
165    ///
166    /// [profile]: https://spec.matrix.org/v1.19/client-server-api/#profiles
167    KeyTooLarge,
168
169    /// `M_LIMIT_EXCEEDED`
170    ///
171    /// The request has been refused due to [rate limiting]: too many requests have been sent in a
172    /// short period of time.
173    ///
174    /// [rate limiting]: https://spec.matrix.org/v1.19/client-server-api/#rate-limiting
175    LimitExceeded(LimitExceededErrorData),
176
177    /// `M_MISSING_PARAM`
178    ///
179    /// A required parameter was missing from the request.
180    MissingParam,
181
182    /// `M_MISSING_TOKEN`
183    ///
184    /// No [access token] was specified for the request, but one is required.
185    ///
186    /// [access token]: https://spec.matrix.org/v1.19/client-server-api/#client-authentication
187    MissingToken,
188
189    /// `M_NOT_FOUND`
190    ///
191    /// No resource was found for this request.
192    NotFound,
193
194    /// `M_NOT_IN_THREAD`
195    ///
196    /// Part of [MSC4306]: an automatic thread subscription was set to an event ID that isn't part
197    /// of the subscribed-to thread.
198    ///
199    /// [MSC4306]: https://github.com/matrix-org/matrix-spec-proposals/pull/4306
200    #[cfg(feature = "unstable-msc4306")]
201    NotInThread,
202
203    /// `M_NOT_JSON`
204    ///
205    /// The request did not contain valid JSON.
206    NotJson,
207
208    /// `M_NOT_YET_UPLOADED`
209    ///
210    /// An `mxc:` URI generated with the `POST /_matrix/media/*/create` endpoint was used and the
211    /// content is not yet available.
212    NotYetUploaded,
213
214    /// `M_PROFILE_TOO_LARGE`
215    ///
216    /// Storing the value in the request would make the [profile] exceed its maximum allowed size
217    /// of 64 KiB.
218    ///
219    /// [profile]: https://spec.matrix.org/v1.19/client-server-api/#profiles
220    ProfileTooLarge,
221
222    /// `M_RESOURCE_LIMIT_EXCEEDED`
223    ///
224    /// The request cannot be completed because the homeserver has reached a resource limit imposed
225    /// on it. For example, a homeserver held in a shared hosting environment may reach a resource
226    /// limit if it starts using too much memory or disk space.
227    ResourceLimitExceeded(ResourceLimitExceededErrorData),
228
229    /// `M_ROOM_IN_USE`
230    ///
231    /// The [room alias] specified in the `POST /_matrix/client/*/createRoom` request is already
232    /// taken.
233    ///
234    /// [room alias]: https://spec.matrix.org/v1.19/client-server-api/#room-aliases
235    RoomInUse,
236
237    /// `M_SENDER_IGNORED`
238    ///
239    /// The sender of the requested event is ignored by the requesting user. ([MSC])
240    ///
241    /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4406
242    #[cfg(feature = "unstable-msc4406")]
243    SenderIgnored(SenderIgnoredErrorData),
244
245    /// `M_SERVER_NOT_TRUSTED`
246    ///
247    /// The client's request used a third-party server, e.g. identity server, that this server does
248    /// not trust.
249    ServerNotTrusted,
250
251    /// `M_THREEPID_AUTH_FAILED`
252    ///
253    /// Authentication could not be performed on the [third-party identifier].
254    ///
255    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
256    ThreepidAuthFailed,
257
258    /// `M_THREEPID_DENIED`
259    ///
260    /// The server does not permit this [third-party identifier]. This may happen if the server
261    /// only permits, for example, email addresses from a particular domain.
262    ///
263    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
264    ThreepidDenied,
265
266    /// `M_THREEPID_IN_USE`
267    ///
268    /// The [third-party identifier] is already in use by another user.
269    ///
270    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
271    ThreepidInUse,
272
273    /// `M_THREEPID_MEDIUM_NOT_SUPPORTED`
274    ///
275    /// The homeserver does not support adding a [third-party identifier] of the given medium.
276    ///
277    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
278    ThreepidMediumNotSupported,
279
280    /// `M_THREEPID_NOT_FOUND`
281    ///
282    /// No account matching the given [third-party identifier] could be found.
283    ///
284    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
285    ThreepidNotFound,
286
287    /// `M_TOKEN_INCORRECT`
288    ///
289    /// The token that the user entered to validate the session is incorrect.
290    TokenIncorrect,
291
292    /// `M_TOO_LARGE`
293    ///
294    /// The request or entity was too large.
295    TooLarge,
296
297    /// `M_UNABLE_TO_AUTHORISE_JOIN`
298    ///
299    /// The room is [restricted] and none of the conditions can be validated by the homeserver.
300    /// This can happen if the homeserver does not know about any of the rooms listed as
301    /// conditions, for example.
302    ///
303    /// [restricted]: https://spec.matrix.org/v1.19/client-server-api/#restricted-rooms
304    UnableToAuthorizeJoin,
305
306    /// `M_UNABLE_TO_GRANT_JOIN`
307    ///
308    /// A different server should be attempted for the join. This is typically because the resident
309    /// server can see that the joining user satisfies one or more conditions, such as in the case
310    /// of [restricted rooms], but the resident server would be unable to meet the authorization
311    /// rules.
312    ///
313    /// [restricted rooms]: https://spec.matrix.org/v1.19/client-server-api/#restricted-rooms
314    UnableToGrantJoin,
315
316    /// `M_UNACTIONABLE`
317    ///
318    /// The server does not want to handle the [federated report].
319    ///
320    /// [federated report]: https://github.com/matrix-org/matrix-spec-proposals/pull/3843
321    #[cfg(feature = "unstable-msc3843")]
322    Unactionable,
323
324    /// `M_UNAUTHORIZED`
325    ///
326    /// The request was not correctly authorized. Usually due to login failures.
327    Unauthorized,
328
329    /// `M_UNKNOWN`
330    ///
331    /// An unknown error has occurred.
332    Unknown,
333
334    /// `M_UNKNOWN_DEVICE`
335    ///
336    /// The device ID supplied by the application service does not belong to the user ID during
337    /// [identity assertion].
338    ///
339    /// [identity assertion]: https://spec.matrix.org/v1.19/application-service-api/#identity-assertion
340    UnknownDevice,
341
342    /// `M_UNKNOWN_POS`
343    ///
344    /// The sliding sync ([MSC4186]) connection was expired by the server.
345    ///
346    /// [MSC4186]: https://github.com/matrix-org/matrix-spec-proposals/pull/4186
347    #[cfg(feature = "unstable-msc4186")]
348    UnknownPos,
349
350    /// `M_UNKNOWN_TOKEN`
351    ///
352    /// The [access or refresh token] specified was not recognized.
353    ///
354    /// [access or refresh token]: https://spec.matrix.org/v1.19/client-server-api/#client-authentication
355    UnknownToken(UnknownTokenErrorData),
356
357    /// `M_UNRECOGNIZED`
358    ///
359    /// The server did not understand the request.
360    ///
361    /// This is expected to be returned with a 404 HTTP status code if the endpoint is not
362    /// implemented or a 405 HTTP status code if the endpoint is implemented, but the incorrect
363    /// HTTP method is used.
364    Unrecognized,
365
366    /// `M_UNSUPPORTED_ROOM_VERSION`
367    ///
368    /// The request to `POST /_matrix/client/*/createRoom` used a room version that the server does
369    /// not support.
370    UnsupportedRoomVersion,
371
372    /// `M_URL_NOT_SET`
373    ///
374    /// The application service doesn't have a URL configured.
375    UrlNotSet,
376
377    /// `M_USER_DEACTIVATED`
378    ///
379    /// The user ID associated with the request has been deactivated.
380    UserDeactivated,
381
382    /// `M_USER_IN_USE`
383    ///
384    /// The desired user ID is already taken.
385    UserInUse,
386
387    /// `M_USER_LIMIT_EXCEEDED`
388    ///
389    /// The request cannot be completed because the user has exceeded (or the request would cause
390    /// them to exceed) a limit associated with their account. For example, a user may have reached
391    /// their allocated storage quota, reached a maximum number of allowed rooms, devices, or other
392    /// account-scoped resources, or exceeded usage limits for specific features.
393    UserLimitExceeded(UserLimitExceededErrorData),
394
395    /// `M_USER_LOCKED`
396    ///
397    /// The account has been [locked] and cannot be used at this time.
398    ///
399    /// [locked]: https://spec.matrix.org/v1.19/client-server-api/#account-locking
400    UserLocked,
401
402    /// `M_USER_SUSPENDED`
403    ///
404    /// The account has been [suspended] and can only be used for limited actions at this time.
405    ///
406    /// [suspended]: https://spec.matrix.org/v1.19/client-server-api/#account-suspension
407    UserSuspended,
408
409    /// `M_WEAK_PASSWORD`
410    ///
411    /// The password was [rejected] by the server for being too weak.
412    ///
413    /// [rejected]: https://spec.matrix.org/v1.19/client-server-api/#password-management
414    WeakPassword,
415
416    /// `M_WRONG_ROOM_KEYS_VERSION`
417    ///
418    /// The version of the [room keys backup] provided in the request does not match the current
419    /// backup version.
420    ///
421    /// [room keys backup]: https://spec.matrix.org/v1.19/client-server-api/#server-side-key-backups
422    WrongRoomKeysVersion(WrongRoomKeysVersionErrorData),
423
424    #[doc(hidden)]
425    _Custom(Box<CustomErrorKind>),
426}
427
428impl ErrorKind {
429    /// Get the [`ErrorCode`] for this `ErrorKind`.
430    pub fn errcode(&self) -> ErrorCode {
431        match self {
432            ErrorKind::AppserviceLoginUnsupported => ErrorCode::AppserviceLoginUnsupported,
433            ErrorKind::BadAlias => ErrorCode::BadAlias,
434            ErrorKind::BadJson => ErrorCode::BadJson,
435            ErrorKind::BadState => ErrorCode::BadState,
436            ErrorKind::BadStatus(_) => ErrorCode::BadStatus,
437            ErrorKind::CannotLeaveServerNoticeRoom => ErrorCode::CannotLeaveServerNoticeRoom,
438            ErrorKind::CannotOverwriteMedia => ErrorCode::CannotOverwriteMedia,
439            ErrorKind::CaptchaInvalid => ErrorCode::CaptchaInvalid,
440            ErrorKind::CaptchaNeeded => ErrorCode::CaptchaNeeded,
441            #[cfg(feature = "unstable-msc4388")]
442            ErrorKind::ConcurrentWrite => ErrorCode::ConcurrentWrite,
443            #[cfg(feature = "unstable-msc4306")]
444            ErrorKind::ConflictingUnsubscription => ErrorCode::ConflictingUnsubscription,
445            ErrorKind::ConnectionFailed => ErrorCode::ConnectionFailed,
446            ErrorKind::ConnectionTimeout => ErrorCode::ConnectionTimeout,
447            ErrorKind::DuplicateAnnotation => ErrorCode::DuplicateAnnotation,
448            ErrorKind::Exclusive => ErrorCode::Exclusive,
449            ErrorKind::Forbidden => ErrorCode::Forbidden,
450            ErrorKind::GuestAccessForbidden => ErrorCode::GuestAccessForbidden,
451            ErrorKind::IncompatibleRoomVersion(_) => ErrorCode::IncompatibleRoomVersion,
452            ErrorKind::InvalidParam => ErrorCode::InvalidParam,
453            ErrorKind::InvalidRoomState => ErrorCode::InvalidRoomState,
454            ErrorKind::InvalidUsername => ErrorCode::InvalidUsername,
455            ErrorKind::InviteBlocked => ErrorCode::InviteBlocked,
456            ErrorKind::KeyTooLarge => ErrorCode::KeyTooLarge,
457            ErrorKind::LimitExceeded(_) => ErrorCode::LimitExceeded,
458            ErrorKind::MissingParam => ErrorCode::MissingParam,
459            ErrorKind::MissingToken => ErrorCode::MissingToken,
460            ErrorKind::NotFound => ErrorCode::NotFound,
461            #[cfg(feature = "unstable-msc4306")]
462            ErrorKind::NotInThread => ErrorCode::NotInThread,
463            ErrorKind::NotJson => ErrorCode::NotJson,
464            ErrorKind::NotYetUploaded => ErrorCode::NotYetUploaded,
465            ErrorKind::ProfileTooLarge => ErrorCode::ProfileTooLarge,
466            ErrorKind::ResourceLimitExceeded(_) => ErrorCode::ResourceLimitExceeded,
467            ErrorKind::RoomInUse => ErrorCode::RoomInUse,
468            #[cfg(feature = "unstable-msc4406")]
469            ErrorKind::SenderIgnored(_) => ErrorCode::SenderIgnored,
470            ErrorKind::ServerNotTrusted => ErrorCode::ServerNotTrusted,
471            ErrorKind::ThreepidAuthFailed => ErrorCode::ThreepidAuthFailed,
472            ErrorKind::ThreepidDenied => ErrorCode::ThreepidDenied,
473            ErrorKind::ThreepidInUse => ErrorCode::ThreepidInUse,
474            ErrorKind::ThreepidMediumNotSupported => ErrorCode::ThreepidMediumNotSupported,
475            ErrorKind::ThreepidNotFound => ErrorCode::ThreepidNotFound,
476            ErrorKind::TokenIncorrect => ErrorCode::TokenIncorrect,
477            ErrorKind::TooLarge => ErrorCode::TooLarge,
478            ErrorKind::UnableToAuthorizeJoin => ErrorCode::UnableToAuthorizeJoin,
479            ErrorKind::UnableToGrantJoin => ErrorCode::UnableToGrantJoin,
480            #[cfg(feature = "unstable-msc3843")]
481            ErrorKind::Unactionable => ErrorCode::Unactionable,
482            ErrorKind::Unauthorized => ErrorCode::Unauthorized,
483            ErrorKind::Unknown => ErrorCode::Unknown,
484            ErrorKind::UnknownDevice => ErrorCode::UnknownDevice,
485            #[cfg(feature = "unstable-msc4186")]
486            ErrorKind::UnknownPos => ErrorCode::UnknownPos,
487            ErrorKind::UnknownToken(_) => ErrorCode::UnknownToken,
488            ErrorKind::Unrecognized => ErrorCode::Unrecognized,
489            ErrorKind::UnsupportedRoomVersion => ErrorCode::UnsupportedRoomVersion,
490            ErrorKind::UrlNotSet => ErrorCode::UrlNotSet,
491            ErrorKind::UserDeactivated => ErrorCode::UserDeactivated,
492            ErrorKind::UserInUse => ErrorCode::UserInUse,
493            ErrorKind::UserLimitExceeded(_) => ErrorCode::UserLimitExceeded,
494            ErrorKind::UserLocked => ErrorCode::UserLocked,
495            ErrorKind::UserSuspended => ErrorCode::UserSuspended,
496            ErrorKind::WeakPassword => ErrorCode::WeakPassword,
497            ErrorKind::WrongRoomKeysVersion(_) => ErrorCode::WrongRoomKeysVersion,
498            ErrorKind::_Custom(kind) => kind.errcode.as_str().into(),
499        }
500    }
501
502    /// Get the JSON data for this `ErrorKind`, if it uses a custom error code.
503    pub fn custom_json_data(&self) -> Option<&JsonObject> {
504        as_variant!(self, Self::_Custom(error_kind) => &error_kind.data)
505    }
506}
507
508/// Data for the `M_BAD_STATUS` [`ErrorKind`].
509#[derive(Clone, Debug, Default, PartialEq, Eq)]
510#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
511pub struct BadStatusErrorData {
512    /// The HTTP status code of the response.
513    pub status: Option<http::StatusCode>,
514
515    /// The body of the response.
516    pub body: Option<String>,
517}
518
519impl BadStatusErrorData {
520    /// Construct a new empty `BadStatusErrorData`.
521    pub fn new() -> Self {
522        Self::default()
523    }
524}
525
526/// Data for the `M_INCOMPATIBLE_ROOM_VERSION` [`ErrorKind`].
527#[derive(Clone, Debug, PartialEq, Eq)]
528#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
529pub struct IncompatibleRoomVersionErrorData {
530    /// The room's version.
531    pub room_version: RoomVersionId,
532}
533
534impl IncompatibleRoomVersionErrorData {
535    /// Construct a new `IncompatibleRoomVersionErrorData` with the given room version.
536    pub fn new(room_version: RoomVersionId) -> Self {
537        Self { room_version }
538    }
539}
540
541/// Data for the `M_LIMIT_EXCEEDED` [`ErrorKind`].
542#[derive(Clone, Debug, Default, PartialEq, Eq)]
543#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
544pub struct LimitExceededErrorData {
545    /// How long a client should wait before they can try again.
546    pub retry_after: Option<RetryAfter>,
547}
548
549impl LimitExceededErrorData {
550    /// Construct a new empty `LimitExceededErrorData`.
551    pub fn new() -> Self {
552        Self::default()
553    }
554}
555
556/// How long a client should wait before it tries again.
557#[derive(Debug, Clone, Copy, PartialEq, Eq)]
558#[allow(clippy::exhaustive_enums)]
559pub enum RetryAfter {
560    /// The client should wait for the given duration.
561    ///
562    /// This variant should be preferred for backwards compatibility, as it will also populate the
563    /// `retry_after_ms` field in the body of the response.
564    Delay(Duration),
565    /// The client should wait for the given date and time.
566    DateTime(SystemTime),
567}
568
569impl TryFrom<&http::HeaderValue> for RetryAfter {
570    type Error = HeaderDeserializationError;
571
572    fn try_from(value: &http::HeaderValue) -> Result<Self, Self::Error> {
573        if value.as_bytes().iter().all(|b| b.is_ascii_digit()) {
574            // It should be a duration.
575            Ok(Self::Delay(Duration::from_secs(u64::from_str(value.to_str()?)?)))
576        } else {
577            // It should be a date.
578            Ok(Self::DateTime(http_date_to_system_time(value)?))
579        }
580    }
581}
582
583impl TryFrom<&RetryAfter> for http::HeaderValue {
584    type Error = HeaderSerializationError;
585
586    fn try_from(value: &RetryAfter) -> Result<Self, Self::Error> {
587        match value {
588            RetryAfter::Delay(duration) => Ok(duration.as_secs().into()),
589            RetryAfter::DateTime(time) => system_time_to_http_date(time),
590        }
591    }
592}
593
594/// Data for the `M_RESOURCE_LIMIT_EXCEEDED` [`ErrorKind`].
595#[derive(Clone, Debug, PartialEq, Eq)]
596#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
597pub struct ResourceLimitExceededErrorData {
598    /// A URI giving a contact method for the server administrator.
599    pub admin_contact: String,
600}
601
602impl ResourceLimitExceededErrorData {
603    /// Construct a new `ResourceLimitExceededErrorData` with the given admin contact URI.
604    pub fn new(admin_contact: String) -> Self {
605        Self { admin_contact }
606    }
607}
608
609/// Data for the `M_SENDER_IGNORED` [`ErrorKind`].
610#[cfg(feature = "unstable-msc4406")]
611#[derive(Clone, Debug, Default, PartialEq, Eq)]
612#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
613pub struct SenderIgnoredErrorData {
614    /// The user who sent the ignored event.
615    pub sender: Option<OwnedUserId>,
616}
617
618#[cfg(feature = "unstable-msc4406")]
619impl SenderIgnoredErrorData {
620    /// Construct a new empty `SenderIgnoredErrorData`.
621    pub fn new() -> Self {
622        Self::default()
623    }
624
625    /// Construct a new `SenderIgnoredErrorData` with the given sender user.
626    pub fn with_sender(sender: OwnedUserId) -> Self {
627        Self { sender: Some(sender) }
628    }
629}
630
631/// Data for the `M_UNKNOWN_TOKEN` [`ErrorKind`].
632#[derive(Clone, Debug, Default, PartialEq, Eq)]
633#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
634pub struct UnknownTokenErrorData {
635    /// If this is `true`, the client is in a "[soft logout]" state, i.e. the server requires
636    /// re-authentication but the session is not invalidated. The client can acquire a new
637    /// access token by specifying the device ID it is already using to the login API.
638    ///
639    /// [soft logout]: https://spec.matrix.org/v1.19/client-server-api/#soft-logout
640    pub soft_logout: bool,
641}
642
643impl UnknownTokenErrorData {
644    /// Construct a new `UnknownTokenErrorData` with `soft_logout` set to `false`.
645    pub fn new() -> Self {
646        Self::default()
647    }
648}
649
650/// Data for the `M_USER_LIMIT_EXCEEDED` [`ErrorKind`].
651#[derive(Clone, Debug, PartialEq, Eq)]
652#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
653pub struct UserLimitExceededErrorData {
654    /// A URI that the client can present to the user to provide more context on the encountered
655    /// limit and, if applicable, guidance on how to increase the limit.
656    ///
657    /// The homeserver MAY return different values depending on the type of limit reached.
658    pub info_uri: String,
659
660    /// Whether the specific limit encountered can be increased.
661    ///
662    /// If `true`, it indicates that the specific limit encountered can be increased, for example
663    /// by upgrading the user’s account tier. If `false`, the limit is a hard limit that cannot be
664    /// increased.
665    ///
666    /// Defaults to `false`.
667    pub can_upgrade: bool,
668}
669
670impl UserLimitExceededErrorData {
671    /// Construct a new `UserLimitExceededErrorData` with the given URI.
672    pub fn new(info_uri: String) -> Self {
673        Self { info_uri, can_upgrade: false }
674    }
675}
676
677/// Data for the `M_WRONG_ROOM_KEYS_VERSION` [`ErrorKind`].
678#[derive(Clone, Debug, PartialEq, Eq)]
679#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
680pub struct WrongRoomKeysVersionErrorData {
681    /// The currently active backup version.
682    pub current_version: String,
683}
684
685impl WrongRoomKeysVersionErrorData {
686    /// Construct a new `WrongRoomKeysVersionErrorData` with the given current active backup
687    /// version.
688    pub fn new(current_version: String) -> Self {
689        Self { current_version }
690    }
691}
692
693/// A custom error kind.
694#[doc(hidden)]
695#[derive(Clone, Debug, PartialEq, Eq)]
696pub struct CustomErrorKind {
697    /// The error code.
698    pub(super) errcode: String,
699
700    /// The data for the error.
701    pub(super) data: JsonObject,
702}
703
704/// The possible [error codes] defined in the Matrix spec.
705///
706/// [error codes]: https://spec.matrix.org/v1.19/client-server-api/#standard-error-response
707#[derive(Clone, StringEnum)]
708#[non_exhaustive]
709#[ruma_enum(rename_all(prefix = "M_", rule = "SCREAMING_SNAKE_CASE"))]
710// Please keep the variants sorted alphabetically.
711pub enum ErrorCode {
712    /// `M_APPSERVICE_LOGIN_UNSUPPORTED`
713    ///
714    /// An application service used the [`m.login.application_service`] type an endpoint from the
715    /// [legacy authentication API] in a way that is not supported by the homeserver, because the
716    /// server only supports the [OAuth 2.0 API].
717    ///
718    /// [`m.login.application_service`]: https://spec.matrix.org/v1.19/application-service-api/#server-admin-style-permissions
719    /// [legacy authentication API]: https://spec.matrix.org/v1.19/client-server-api/#legacy-api
720    /// [OAuth 2.0 API]: https://spec.matrix.org/v1.19/client-server-api/#oauth-20-api
721    AppserviceLoginUnsupported,
722
723    /// `M_BAD_ALIAS`
724    ///
725    /// One or more [room aliases] within the `m.room.canonical_alias` event do not point to the
726    /// room ID for which the state event is to be sent to.
727    ///
728    /// [room aliases]: https://spec.matrix.org/v1.19/client-server-api/#room-aliases
729    BadAlias,
730
731    /// `M_BAD_JSON`
732    ///
733    /// The request contained valid JSON, but it was malformed in some way, e.g. missing required
734    /// keys, invalid values for keys.
735    BadJson,
736
737    /// `M_BAD_STATE`
738    ///
739    /// The state change requested cannot be performed, such as attempting to unban a user who is
740    /// not banned.
741    BadState,
742
743    /// `M_BAD_STATUS`
744    ///
745    /// The application service returned a bad status.
746    BadStatus,
747
748    /// `M_CANNOT_LEAVE_SERVER_NOTICE_ROOM`
749    ///
750    /// The user is unable to reject an invite to join the [server notices] room.
751    ///
752    /// [server notices]: https://spec.matrix.org/v1.19/client-server-api/#server-notices
753    CannotLeaveServerNoticeRoom,
754
755    /// `M_CANNOT_OVERWRITE_MEDIA`
756    ///
757    /// The `PUT /_matrix/media/*/upload/{serverName}/{mediaId}` endpoint was called with a media ID
758    /// that already has content.
759    CannotOverwriteMedia,
760
761    /// `M_CAPTCHA_INVALID`
762    ///
763    /// The Captcha provided did not match what was expected.
764    CaptchaInvalid,
765
766    /// `M_CAPTCHA_NEEDED`
767    ///
768    /// A Captcha is required to complete the request.
769    CaptchaNeeded,
770
771    /// `M_CONCURRENT_WRITE`
772    ///
773    /// The sequence token provided when updating a rendezvous session
774    /// does not match the current sequence token.
775    ///
776    /// This uses the unstable prefix defined in [MSC4388].
777    ///
778    /// [MSC4388]: https://github.com/matrix-org/matrix-spec-proposals/pull/4388
779    #[cfg(feature = "unstable-msc4388")]
780    #[ruma_enum(rename = "IO_ELEMENT_MSC4388_CONCURRENT_WRITE")]
781    ConcurrentWrite,
782
783    /// `M_CONFLICTING_UNSUBSCRIPTION`
784    ///
785    /// Part of [MSC4306]: an automatic thread subscription has been skipped by the server, because
786    /// the user unsubsubscribed after the indicated subscribed-to event.
787    ///
788    /// [MSC4306]: https://github.com/matrix-org/matrix-spec-proposals/pull/4306
789    #[cfg(feature = "unstable-msc4306")]
790    #[ruma_enum(rename = "IO.ELEMENT.MSC4306.M_CONFLICTING_UNSUBSCRIPTION")]
791    ConflictingUnsubscription,
792
793    /// `M_CONNECTION_FAILED`
794    ///
795    /// The connection to the application service failed.
796    ConnectionFailed,
797
798    /// `M_CONNECTION_TIMEOUT`
799    ///
800    /// The connection to the application service timed out.
801    ConnectionTimeout,
802
803    /// `M_DUPLICATE_ANNOTATION`
804    ///
805    /// The request is an attempt to send a [duplicate annotation].
806    ///
807    /// [duplicate annotation]: https://spec.matrix.org/v1.19/client-server-api/#avoiding-duplicate-annotations
808    DuplicateAnnotation,
809
810    /// `M_EXCLUSIVE`
811    ///
812    /// The resource being requested is reserved by an application service, or the application
813    /// service making the request has not created the resource.
814    Exclusive,
815
816    /// `M_FORBIDDEN`
817    ///
818    /// Forbidden access, e.g. joining a room without permission, failed login.
819    Forbidden,
820
821    /// `M_GUEST_ACCESS_FORBIDDEN`
822    ///
823    /// The room or resource does not permit [guests] to access it.
824    ///
825    /// [guests]: https://spec.matrix.org/v1.19/client-server-api/#guest-access
826    GuestAccessForbidden,
827
828    /// `M_INCOMPATIBLE_ROOM_VERSION`
829    ///
830    /// The client attempted to join a room that has a version the server does not support.
831    IncompatibleRoomVersion,
832
833    /// `M_INVALID_PARAM`
834    ///
835    /// A parameter that was specified has the wrong value. For example, the server expected an
836    /// integer and instead received a string.
837    InvalidParam,
838
839    /// `M_INVALID_ROOM_STATE`
840    ///
841    /// The initial state implied by the parameters to the `POST /_matrix/client/*/createRoom`
842    /// request is invalid, e.g. the user's `power_level` is set below that necessary to set the
843    /// room name.
844    InvalidRoomState,
845
846    /// `M_INVALID_USERNAME`
847    ///
848    /// The desired user name is not valid.
849    InvalidUsername,
850
851    /// `M_INVITE_BLOCKED`
852    ///
853    /// The invite was interdicted by moderation tools or configured access controls without having
854    /// been witnessed by the invitee.
855    ///
856    /// Unstable prefix intentionally shared with MSC4155 for compatibility.
857    #[ruma_enum(alias = "ORG.MATRIX.MSC4155.INVITE_BLOCKED")]
858    InviteBlocked,
859
860    /// `M_KEY_TOO_LARGE`
861    ///
862    /// The [profile] key in the request exceeds the maximum allowed length of 255 bytes.
863    ///
864    /// [profile]: https://spec.matrix.org/v1.19/client-server-api/#profiles
865    KeyTooLarge,
866
867    /// `M_LIMIT_EXCEEDED`
868    ///
869    /// The request has been refused due to [rate limiting]: too many requests have been sent in a
870    /// short period of time.
871    ///
872    /// [rate limiting]: https://spec.matrix.org/v1.19/client-server-api/#rate-limiting
873    LimitExceeded,
874
875    /// `M_MISSING_PARAM`
876    ///
877    /// A required parameter was missing from the request.
878    MissingParam,
879
880    /// `M_MISSING_TOKEN`
881    ///
882    /// No [access token] was specified for the request, but one is required.
883    ///
884    /// [access token]: https://spec.matrix.org/v1.19/client-server-api/#client-authentication
885    MissingToken,
886
887    /// `M_NOT_FOUND`
888    ///
889    /// No resource was found for this request.
890    NotFound,
891
892    /// `M_NOT_IN_THREAD`
893    ///
894    /// Part of [MSC4306]: an automatic thread subscription was set to an event ID that isn't part
895    /// of the subscribed-to thread.
896    ///
897    /// [MSC4306]: https://github.com/matrix-org/matrix-spec-proposals/pull/4306
898    #[cfg(feature = "unstable-msc4306")]
899    #[ruma_enum(rename = "IO.ELEMENT.MSC4306.M_NOT_IN_THREAD")]
900    NotInThread,
901
902    /// `M_NOT_JSON`
903    ///
904    /// The request did not contain valid JSON.
905    NotJson,
906
907    /// `M_NOT_YET_UPLOADED`
908    ///
909    /// An `mxc:` URI generated with the `POST /_matrix/media/*/create` endpoint was used and the
910    /// content is not yet available.
911    NotYetUploaded,
912
913    /// `M_PROFILE_TOO_LARGE`
914    ///
915    /// Storing the value in the request would make the [profile] exceed its maximum allowed size
916    /// of 64 KiB.
917    ///
918    /// [profile]: https://spec.matrix.org/v1.19/client-server-api/#profiles
919    ProfileTooLarge,
920
921    /// `M_RESOURCE_LIMIT_EXCEEDED`
922    ///
923    /// The request cannot be completed because the homeserver has reached a resource limit imposed
924    /// on it. For example, a homeserver held in a shared hosting environment may reach a resource
925    /// limit if it starts using too much memory or disk space.
926    ResourceLimitExceeded,
927
928    /// `M_ROOM_IN_USE`
929    ///
930    /// The [room alias] specified in the `POST /_matrix/client/*/createRoom` request is already
931    /// taken.
932    ///
933    /// [room alias]: https://spec.matrix.org/v1.19/client-server-api/#room-aliases
934    RoomInUse,
935
936    /// `M_SENDER_IGNORED`
937    ///
938    /// The sender of the requested event is ignored by the requesting user. ([MSC])
939    ///
940    /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4406
941    #[cfg(feature = "unstable-msc4406")]
942    #[ruma_enum(rename = "UK.TIMEDOUT.MSC4406.SENDER_IGNORED")]
943    SenderIgnored,
944
945    /// `M_SERVER_NOT_TRUSTED`
946    ///
947    /// The client's request used a third-party server, e.g. identity server, that this server does
948    /// not trust.
949    ServerNotTrusted,
950
951    /// `M_THREEPID_AUTH_FAILED`
952    ///
953    /// Authentication could not be performed on the [third-party identifier].
954    ///
955    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
956    ThreepidAuthFailed,
957
958    /// `M_THREEPID_DENIED`
959    ///
960    /// The server does not permit this [third-party identifier]. This may happen if the server
961    /// only permits, for example, email addresses from a particular domain.
962    ///
963    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
964    ThreepidDenied,
965
966    /// `M_THREEPID_IN_USE`
967    ///
968    /// The [third-party identifier] is already in use by another user.
969    ///
970    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
971    ThreepidInUse,
972
973    /// `M_THREEPID_MEDIUM_NOT_SUPPORTED`
974    ///
975    /// The homeserver does not support adding a [third-party identifier] of the given medium.
976    ///
977    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
978    ThreepidMediumNotSupported,
979
980    /// `M_THREEPID_NOT_FOUND`
981    ///
982    /// No account matching the given [third-party identifier] could be found.
983    ///
984    /// [third-party identifier]: https://spec.matrix.org/v1.19/client-server-api/#adding-account-administrative-contact-information
985    ThreepidNotFound,
986
987    /// `M_TOKEN_INCORRECT`
988    ///
989    /// The token that the user entered to validate the session is incorrect.
990    TokenIncorrect,
991
992    /// `M_TOO_LARGE`
993    ///
994    /// The request or entity was too large.
995    TooLarge,
996
997    /// `M_UNABLE_TO_AUTHORISE_JOIN`
998    ///
999    /// The room is [restricted] and none of the conditions can be validated by the homeserver.
1000    /// This can happen if the homeserver does not know about any of the rooms listed as
1001    /// conditions, for example.
1002    ///
1003    /// [restricted]: https://spec.matrix.org/v1.19/client-server-api/#restricted-rooms
1004    #[ruma_enum(rename = "M_UNABLE_TO_AUTHORISE_JOIN")]
1005    UnableToAuthorizeJoin,
1006
1007    /// `M_UNABLE_TO_GRANT_JOIN`
1008    ///
1009    /// A different server should be attempted for the join. This is typically because the resident
1010    /// server can see that the joining user satisfies one or more conditions, such as in the case
1011    /// of [restricted rooms], but the resident server would be unable to meet the authorization
1012    /// rules.
1013    ///
1014    /// [restricted rooms]: https://spec.matrix.org/v1.19/client-server-api/#restricted-rooms
1015    UnableToGrantJoin,
1016
1017    /// `M_UNACTIONABLE`
1018    ///
1019    /// The server does not want to handle the [federated report].
1020    ///
1021    /// [federated report]: https://github.com/matrix-org/matrix-spec-proposals/pull/3843
1022    #[cfg(feature = "unstable-msc3843")]
1023    Unactionable,
1024
1025    /// `M_UNAUTHORIZED`
1026    ///
1027    /// The request was not correctly authorized. Usually due to login failures.
1028    Unauthorized,
1029
1030    /// `M_UNKNOWN`
1031    ///
1032    /// An unknown error has occurred.
1033    Unknown,
1034
1035    /// `M_UNKNOWN_DEVICE`
1036    ///
1037    /// The device ID supplied by the application service does not belong to the user ID during
1038    /// [identity assertion].
1039    ///
1040    /// [identity assertion]: https://spec.matrix.org/v1.19/application-service-api/#identity-assertion
1041    UnknownDevice,
1042
1043    /// `M_UNKNOWN_POS`
1044    ///
1045    /// The sliding sync ([MSC4186]) connection was expired by the server.
1046    ///
1047    /// [MSC4186]: https://github.com/matrix-org/matrix-spec-proposals/pull/4186
1048    #[cfg(feature = "unstable-msc4186")]
1049    UnknownPos,
1050
1051    /// `M_UNKNOWN_TOKEN`
1052    ///
1053    /// The [access or refresh token] specified was not recognized.
1054    ///
1055    /// [access or refresh token]: https://spec.matrix.org/v1.19/client-server-api/#client-authentication
1056    UnknownToken,
1057
1058    /// `M_UNRECOGNIZED`
1059    ///
1060    /// The server did not understand the request.
1061    ///
1062    /// This is expected to be returned with a 404 HTTP status code if the endpoint is not
1063    /// implemented or a 405 HTTP status code if the endpoint is implemented, but the incorrect
1064    /// HTTP method is used.
1065    Unrecognized,
1066
1067    /// `M_UNSUPPORTED_ROOM_VERSION`
1068    UnsupportedRoomVersion,
1069
1070    /// `M_URL_NOT_SET`
1071    ///
1072    /// The application service doesn't have a URL configured.
1073    UrlNotSet,
1074
1075    /// `M_USER_DEACTIVATED`
1076    ///
1077    /// The user ID associated with the request has been deactivated.
1078    UserDeactivated,
1079
1080    /// `M_USER_IN_USE`
1081    ///
1082    /// The desired user ID is already taken.
1083    UserInUse,
1084
1085    /// `M_USER_LIMIT_EXCEEDED`
1086    ///
1087    /// The request cannot be completed because the user has exceeded (or the request would cause
1088    /// them to exceed) a limit associated with their account. For example, a user may have reached
1089    /// their allocated storage quota, reached a maximum number of allowed rooms, devices, or other
1090    /// account-scoped resources, or exceeded usage limits for specific features.
1091    UserLimitExceeded,
1092
1093    /// `M_USER_LOCKED`
1094    ///
1095    /// The account has been [locked] and cannot be used at this time.
1096    ///
1097    /// [locked]: https://spec.matrix.org/v1.19/client-server-api/#account-locking
1098    UserLocked,
1099
1100    /// `M_USER_SUSPENDED`
1101    ///
1102    /// The account has been [suspended] and can only be used for limited actions at this time.
1103    ///
1104    /// [suspended]: https://spec.matrix.org/v1.19/client-server-api/#account-suspension
1105    UserSuspended,
1106
1107    /// `M_WEAK_PASSWORD`
1108    ///
1109    /// The password was [rejected] by the server for being too weak.
1110    ///
1111    /// [rejected]: https://spec.matrix.org/v1.19/client-server-api/#password-management
1112    WeakPassword,
1113
1114    /// `M_WRONG_ROOM_KEYS_VERSION`
1115    ///
1116    /// The version of the [room keys backup] provided in the request does not match the current
1117    /// backup version.
1118    ///
1119    /// [room keys backup]: https://spec.matrix.org/v1.19/client-server-api/#server-side-key-backups
1120    WrongRoomKeysVersion,
1121
1122    #[doc(hidden)]
1123    _Custom(PrivOwnedStr),
1124}
1125
1126#[cfg(test)]
1127mod tests {
1128    use super::ErrorKind;
1129
1130    #[test]
1131    fn test_error_kind_type_size() {
1132        // There is no strict requirement for this type to be 40 bytes or smaller,
1133        // but it's been optimized by hand (Boxing the `_Custom` variant)
1134        // and it would be nice to keep track of any regressions.
1135        let size = size_of::<ErrorKind>();
1136        assert!(size <= 40, "size_of::<ErrorKind>() has regressed, is now {size}");
1137    }
1138}