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}