teloxide_core/errors.rs
1//! Possible error types.
2
3use std::{io, sync::Arc};
4
5use thiserror::Error;
6
7use crate::types::{ChatId, ResponseParameters, Seconds};
8
9/// An error caused by sending a request to Telegram.
10#[derive(Debug, Error, Clone)]
11pub enum RequestError {
12 /// A Telegram API error.
13 #[error("A Telegram's error: {0}")]
14 Api(#[from] ApiError),
15
16 /// The group has been migrated to a supergroup with the specified
17 /// identifier.
18 #[error("The group has been migrated to a supergroup with ID #{0}")]
19 MigrateToChatId(ChatId),
20
21 /// In case of exceeding flood control, the number of seconds left to wait
22 /// before the request can be repeated.
23 #[error("Retry after {0}")]
24 RetryAfter(Seconds),
25
26 /// Network error while sending a request to Telegram.
27 #[error("A network error: {0}")]
28 // NOTE: this variant must not be created by anything except the explicit From impl
29 Network(#[source] Arc<reqwest::Error>),
30
31 /// Error while parsing a response from Telegram.
32 ///
33 /// If you've received this error, please, [open an issue] with the
34 /// description of the error.
35 ///
36 /// [open an issue]: https://github.com/teloxide/teloxide/issues/new
37 #[error("An error while parsing JSON: {source} (raw: {raw:?})")]
38 InvalidJson {
39 #[source]
40 source: Arc<serde_json::Error>,
41 /// The raw string JSON that couldn't been parsed
42 raw: Box<str>,
43 },
44
45 /// Occurs when trying to send a file to Telegram.
46 #[error("An I/O error: {0}")]
47 Io(#[from] Arc<io::Error>),
48}
49
50/// An error caused by downloading a file.
51#[derive(Debug, Error, Clone)]
52pub enum DownloadError {
53 /// A network error while downloading a file from Telegram.
54 #[error("A network error: {0}")]
55 // NOTE: this variant must not be created by anything except the explicit From impl
56 Network(#[source] Arc<reqwest::Error>),
57
58 /// An I/O error while writing a file to destination.
59 #[error("An I/O error: {0}")]
60 Io(#[from] Arc<std::io::Error>),
61}
62
63pub trait AsResponseParameters {
64 fn response_parameters(&self) -> Option<ResponseParameters>;
65
66 fn retry_after(&self) -> Option<Seconds> {
67 self.response_parameters().and_then(|rp| match rp {
68 ResponseParameters::RetryAfter(n) => Some(n),
69 _ => None,
70 })
71 }
72
73 fn migrate_to_chat_id(&self) -> Option<ChatId> {
74 self.response_parameters().and_then(|rp| match rp {
75 ResponseParameters::MigrateToChatId(id) => Some(id),
76 _ => None,
77 })
78 }
79}
80
81impl AsResponseParameters for crate::RequestError {
82 fn response_parameters(&self) -> Option<ResponseParameters> {
83 match *self {
84 Self::RetryAfter(n) => Some(ResponseParameters::RetryAfter(n)),
85 Self::MigrateToChatId(id) => Some(ResponseParameters::MigrateToChatId(id)),
86 _ => None,
87 }
88 }
89}
90
91macro_rules! impl_api_error {
92 (
93 $( #[$meta:meta] )*
94 $vis:vis enum $ident:ident {
95 $(
96 $( #[$var_meta:meta] )*
97 $var_name:ident $( ($var_inner:ty) )? = $var_string:literal $(with $var_parser:expr)?
98 ),*
99 }
100 ) => {
101
102 $(#[$meta])*
103 #[derive(Error)]
104 $vis enum $ident {
105 $(
106 $(#[$var_meta])*
107 #[error($var_string)]
108 $var_name $(($var_inner))*,
109 )*
110 }
111
112 const _: () = {
113 struct Visitor;
114
115 impl<'de> ::serde::de::Visitor<'de> for Visitor {
116 type Value = $ident;
117
118 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
119 formatter.write_str("telegram api error string")
120 }
121
122 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
123 where
124 E: ::serde::de::Error,
125 {
126 $(impl_api_error!(@de v, $var_name $( ($var_inner) )?, $var_string $(, $var_parser)*);)*
127 Err(E::unknown_variant(v, &[]))
128 }
129 }
130
131 impl<'de> ::serde::de::Deserialize<'de> for $ident {
132 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133 where
134 D: ::serde::de::Deserializer<'de>,
135 {
136 deserializer.deserialize_str(Visitor)
137 }
138 }
139 };
140 };
141 (@de $value:ident, $variant:ident, $val:literal) => {
142 if $value == $val {
143 return Ok(Self::Value::$variant)
144 }
145 };
146 (@de $value:ident, $variant:ident ($var_inner:ty), $val:literal, $block:expr) => {
147 #[allow(clippy::redundant_closure_call)]
148 match $block($value) {
149 Some(data) => return Ok(Self::Value::$variant(data)),
150 _ => {}
151 }
152 };
153 (@de $value:ident, $variant:ident, $val:literal, $block:expr) => {
154 #[allow(clippy::redundant_closure_call)]
155 if $block($value) {
156 return Ok(Self::Value::$variant);
157 }
158 };
159}
160
161impl_api_error! {
162 /// A kind of an API error.
163 #[derive(Debug, PartialEq, Hash, Eq, Clone)]
164 #[non_exhaustive]
165 pub enum ApiError {
166 /// Occurs when the bot tries to send message to user who blocked the bot.
167 BotBlocked = "Forbidden: bot was blocked by the user",
168
169 /// Occurs when the bot token is invalid.
170 // N.B. These errors are actually slightly different, "Unauthorized" is when the bot token
171 // is formatted mostly right, but is incorrect, whereas "Not Found" is when the url is
172 // not handled by TBA at all. From user POV both of those are "token is invalid", but
173 // there might be some cases where this is not right...
174 InvalidToken = "Invalid bot token" with |text: &str| text == "Unauthorized" || text == "Not Found",
175
176 /// Occurs when bot tries to modify a message without modification content.
177 ///
178 /// May happen in methods:
179 /// 1. [`EditMessageText`]
180 ///
181 /// [`EditMessageText`]: crate::payloads::EditMessageText
182 MessageNotModified = "Bad Request: message is not modified: specified new message content and reply markup are \
183 exactly the same as a current content and reply markup of the message",
184
185 /// Occurs when bot tries to forward or delete a message which was deleted.
186 ///
187 /// May happen in methods:
188 /// 1. [`ForwardMessage`]
189 /// 2. [`DeleteMessage`]
190 ///
191 /// [`ForwardMessage`]: crate::payloads::ForwardMessage
192 /// [`DeleteMessage`]: crate::payloads::DeleteMessage
193 MessageIdInvalid = "Bad Request: MESSAGE_ID_INVALID",
194
195 /// Occurs when bot tries to forward a message which does not exists.
196 ///
197 /// May happen in methods:
198 /// 1. [`ForwardMessage`]
199 ///
200 /// [`ForwardMessage`]: crate::payloads::ForwardMessage
201 MessageToForwardNotFound = "Bad Request: message to forward not found",
202
203 /// Occurs when bot tries to delete a message which does not exists.
204 ///
205 /// May happen in methods:
206 /// 1. [`DeleteMessage`]
207 ///
208 /// [`DeleteMessage`]: crate::payloads::DeleteMessage
209 MessageToDeleteNotFound = "Bad Request: message to delete not found",
210
211 /// Occurs when bot tries to copy a message which does not exists.
212 /// May happen in methods:
213 /// 1. [`CopyMessage`]
214 ///
215 /// [`CopyMessage`]: crate::payloads::CopyMessage
216 MessageToCopyNotFound = "Bad Request: message to copy not found",
217
218 /// Occurs when bot tries to send a text message without text.
219 ///
220 /// May happen in methods:
221 /// 1. [`SendMessage`]
222 ///
223 /// [`SendMessage`]: crate::payloads::SendMessage
224 MessageTextIsEmpty = "Bad Request: message text is empty",
225
226 /// Occurs when bot tries to edit a message after long time.
227 ///
228 /// May happen in methods:
229 /// 1. [`EditMessageText`]
230 ///
231 /// [`EditMessageText`]: crate::payloads::EditMessageText
232 MessageCantBeEdited = "Bad Request: message can't be edited",
233
234 /// Occurs when bot tries to delete a someone else's message in group where
235 /// it does not have enough rights.
236 ///
237 /// May happen in methods:
238 /// 1. [`DeleteMessage`]
239 ///
240 /// [`DeleteMessage`]: crate::payloads::DeleteMessage
241 MessageCantBeDeleted = "Bad Request: message can't be deleted",
242
243 /// Occurs when bot tries to edit a message which does not exists.
244 ///
245 /// May happen in methods:
246 /// 1. [`EditMessageText`]
247 ///
248 /// [`EditMessageText`]: crate::payloads::EditMessageText
249 MessageToEditNotFound = "Bad Request: message to edit not found",
250
251 /// Occurs when bot tries to reply to a message which does not exists.
252 ///
253 /// May happen in methods:
254 /// 1. [`SendMessage`]
255 ///
256 /// [`SendMessage`]: crate::payloads::SendMessage
257 MessageToReplyNotFound = "Bad Request: message to be replied not found",
258
259 /// Occurs when bot tries to
260 MessageIdentifierNotSpecified = "Bad Request: message identifier is not specified",
261
262 /// Occurs when bot tries to send a message with text size greater then
263 /// 4096 symbols.
264 ///
265 /// May happen in methods:
266 /// 1. [`SendMessage`]
267 ///
268 /// [`SendMessage`]: crate::payloads::SendMessage
269 MessageIsTooLong = "Bad Request: message is too long",
270
271 /// Occurs when bot tries to edit a message with text size greater then
272 /// 4096 symbols.
273 ///
274 /// May happen in methods:
275 /// 1. [`EditMessageText`]
276 /// 2. [`EditMessageTextInline`]
277 /// 3. [`EditMessageCaption`]
278 /// 4. [`EditMessageCaptionInline`]
279 ///
280 /// [`EditMessageText`]: crate::payloads::EditMessageText
281 /// [`EditMessageTextInline`]: crate::payloads::EditMessageTextInline
282 /// [`EditMessageCaption`]: crate::payloads::EditMessageCaption
283 /// [`EditMessageCaptionInline`]: crate::payloads::EditMessageCaptionInline
284 EditedMessageIsTooLong = "Bad Request: MESSAGE_TOO_LONG",
285
286 /// Occurs when bot tries to send media group with more than 10 items.
287 ///
288 /// May happen in methods:
289 /// 1. [`SendMediaGroup`]
290 ///
291 /// [`SendMediaGroup`]: crate::payloads::SendMediaGroup
292 TooMuchMessages = "Bad Request: Too much messages to send as an album",
293
294 /// Occurs when bot tries to answer an inline query with more than 50
295 /// results.
296 ///
297 /// Consider using offsets to paginate results.
298 ///
299 /// May happen in methods:
300 /// 1. [`AnswerInlineQuery`]
301 ///
302 /// [`AnswerInlineQuery`]: crate::payloads::AnswerInlineQuery
303 TooMuchInlineQueryResults = "Bad Request: RESULTS_TOO_MUCH",
304
305 /// Occurs when bot tries to stop poll that has already been stopped.
306 ///
307 /// May happen in methods:
308 /// 1. [`SendPoll`]
309 ///
310 /// [`SendPoll`]: crate::payloads::SendPoll
311 PollHasAlreadyClosed = "Bad Request: poll has already been closed",
312
313 /// Occurs when bot tries to send poll with less than 2 options.
314 ///
315 /// May happen in methods:
316 /// 1. [`SendPoll`]
317 ///
318 /// [`SendPoll`]: crate::payloads::SendPoll
319 PollMustHaveMoreOptions = "Bad Request: poll must have at least 2 option",
320
321 /// Occurs when bot tries to send poll with more than 12 options.
322 ///
323 /// May happen in methods:
324 /// 1. [`SendPoll`]
325 ///
326 /// [`SendPoll`]: crate::payloads::SendPoll
327 PollCantHaveMoreOptions = "Bad Request: poll can't have more than 12 options",
328
329 /// Occurs when bot tries to send poll with empty option (without text).
330 ///
331 /// May happen in methods:
332 /// 1. [`SendPoll`]
333 ///
334 /// [`SendPoll`]: crate::payloads::SendPoll
335 PollOptionsMustBeNonEmpty = "Bad Request: poll options must be non-empty",
336
337 /// Occurs when bot tries to send poll with empty question (without text).
338 ///
339 /// May happen in methods:
340 /// 1. [`SendPoll`]
341 ///
342 /// [`SendPoll`]: crate::payloads::SendPoll
343 PollQuestionMustBeNonEmpty = "Bad Request: poll question must be non-empty",
344
345 /// Occurs when bot tries to send poll with total size of options more than
346 /// 100 symbols.
347 ///
348 /// May happen in methods:
349 /// 1. [`SendPoll`]
350 ///
351 /// [`SendPoll`]: crate::payloads::SendPoll
352 PollOptionsLengthTooLong = "Bad Request: poll options length must not exceed 100",
353
354 /// Occurs when bot tries to send poll with question size more than 255
355 /// symbols.
356 ///
357 /// May happen in methods:
358 /// 1. [`SendPoll`]
359 ///
360 /// [`SendPoll`]: crate::payloads::SendPoll
361 PollQuestionLengthTooLong = "Bad Request: poll question length must not exceed 255",
362
363 /// Occurs when bot tries to stop poll with message without poll.
364 ///
365 /// May happen in methods:
366 /// 1. [`StopPoll`]
367 ///
368 /// [`StopPoll`]: crate::payloads::StopPoll
369 MessageWithPollNotFound = "Bad Request: message with poll to stop not found",
370
371 /// Occurs when bot tries to stop poll with message without poll.
372 ///
373 /// May happen in methods:
374 /// 1. [`StopPoll`]
375 ///
376 /// [`StopPoll`]: crate::payloads::StopPoll
377 MessageIsNotAPoll = "Bad Request: message is not a poll",
378
379 /// Occurs when bot tries to send a message to chat in which it is not a
380 /// member.
381 ///
382 /// May happen in methods:
383 /// 1. [`SendMessage`]
384 ///
385 /// [`SendMessage`]: crate::payloads::SendMessage
386 ChatNotFound = "Bad Request: chat not found",
387
388 /// Occurs when bot tries to send method with unknown user_id.
389 ///
390 /// May happen in methods:
391 /// 1. [`getUserProfilePhotos`]
392 ///
393 /// [`getUserProfilePhotos`]:
394 /// crate::payloads::GetUserProfilePhotos
395 UserNotFound = "Bad Request: user not found",
396
397 /// Occurs when bot tries to send [`SetChatDescription`] with same text as
398 /// in the current description.
399 ///
400 /// May happen in methods:
401 /// 1. [`SetChatDescription`]
402 ///
403 /// [`SetChatDescription`]: crate::payloads::SetChatDescription
404 ChatDescriptionIsNotModified = "Bad Request: chat description is not modified",
405
406 /// Occurs when bot tries to answer to query after timeout expire.
407 ///
408 /// May happen in methods:
409 /// 1. [`AnswerCallbackQuery`]
410 ///
411 /// [`AnswerCallbackQuery`]: crate::payloads::AnswerCallbackQuery
412 InvalidQueryId = "Bad Request: query is too old and response timeout expired or query id is invalid",
413
414 /// Occurs when bot tries to send InlineKeyboardMarkup with invalid button
415 /// url.
416 ///
417 /// May happen in methods:
418 /// 1. [`SendMessage`]
419 ///
420 /// [`SendMessage`]: crate::payloads::SendMessage
421 ButtonUrlInvalid = "Bad Request: BUTTON_URL_INVALID",
422
423 /// Occurs when bot tries to send button with data size more than 64 bytes.
424 ///
425 /// May happen in methods:
426 /// 1. [`SendMessage`]
427 ///
428 /// [`SendMessage`]: crate::payloads::SendMessage
429 ButtonDataInvalid = "Bad Request: BUTTON_DATA_INVALID",
430
431 /// Occurs when bot tries to send button with data size == 0.
432 ///
433 /// May happen in methods:
434 /// 1. [`SendMessage`]
435 ///
436 /// [`SendMessage`]: crate::payloads::SendMessage
437 TextButtonsAreUnallowed = "Bad Request: can't parse inline keyboard button: Text buttons are unallowed in the \
438 inline keyboard",
439
440 /// Occurs when bot tries to get file by wrong file id.
441 ///
442 /// May happen in methods:
443 /// 1. [`GetFile`]
444 ///
445 /// [`GetFile`]: crate::payloads::GetFile
446 WrongFileId = "Bad Request: wrong file id",
447
448 /// Occurs when bot tries to send files with wrong file identifier or HTTP
449 /// url
450 WrongFileIdOrUrl = "Bad Request: wrong file identifier/HTTP URL specified",
451
452 /// Occurs when When sending files with an url to a site that doesn't
453 /// respond.
454 FailedToGetUrlContent = "Bad Request: failed to get HTTP URL content",
455
456 /// Occurs when bot tries to do some with group which was deactivated.
457 GroupDeactivated = "Bad Request: group is deactivated",
458
459 /// Occurs when image processing fails on telegram's side.
460 ///
461 /// This is likely caused by an incorrectly encoded image, make sure that
462 /// the image is correctly encoded in a format telegram accepts.
463 ImageProcessFailed = "Bad Request: IMAGE_PROCESS_FAILED",
464
465 /// Occurs when bot tries to set chat photo from file ID
466 ///
467 /// May happen in methods:
468 /// 1. [`SetChatPhoto`]
469 ///
470 /// [`SetChatPhoto`]: crate::payloads::SetChatPhoto
471 PhotoAsInputFileRequired = "Bad Request: Photo should be uploaded as an InputFile",
472
473 /// Occurs when bot tries to add sticker to stickerset by invalid name.
474 ///
475 /// May happen in methods:
476 /// 1. [`AddStickerToSet`]
477 ///
478 /// [`AddStickerToSet`]: crate::payloads::AddStickerToSet
479 InvalidStickersSet = "Bad Request: STICKERSET_INVALID",
480
481 /// Occurs when bot tries to create a sticker set with a name that is
482 /// already used by another sticker set.
483 ///
484 /// May happen in methods:
485 /// 1. [`CreateNewStickerSet`]
486 ///
487 /// [`CreateNewStickerSet`]: crate::payloads::CreateNewStickerSet
488 StickerSetNameOccupied = "Bad Request: sticker set name is already occupied",
489
490 /// Occurs when bot tries to create a sticker set with user id of a bot.
491 ///
492 /// May happen in methods:
493 /// 1. [`CreateNewStickerSet`]
494 ///
495 /// [`CreateNewStickerSet`]: crate::payloads::CreateNewStickerSet
496 StickerSetOwnerIsBot = "Bad Request: USER_IS_BOT",
497
498 /// Occurs when bot tries to create a sticker set with invalid name.
499 ///
500 /// From documentation of [`CreateNewStickerSet`]:
501 /// > Short name of sticker set, to be used in `t.me/addstickers/` URLs
502 /// (e.g., _animals_). Can contain only english letters, digits and
503 /// underscores. Must begin with a letter, can't contain consecutive
504 /// underscores and must end in “\_by\_<bot\_username>”. <bot\_username>
505 /// is case insensitive. 1-64 characters.
506 ///
507 /// May happen in methods:
508 /// 1. [`CreateNewStickerSet`]
509 ///
510 /// [`CreateNewStickerSet`]: crate::payloads::CreateNewStickerSet
511 InvalidStickerName = "Bad Request: invalid sticker set name is specified",
512
513 /// Occurs when bot tries to pin a message without rights to pin in this
514 /// chat.
515 ///
516 /// May happen in methods:
517 /// 1. [`PinChatMessage`]
518 ///
519 /// [`PinChatMessage`]: crate::payloads::PinChatMessage
520 NotEnoughRightsToPinMessage = "Bad Request: not enough rights to pin a message",
521
522 /// Occurs when bot tries to pin or unpin a message without rights to pin
523 /// in this chat.
524 ///
525 /// May happen in methods:
526 /// 1. [`PinChatMessage`]
527 /// 2. [`UnpinChatMessage`]
528 ///
529 /// [`PinChatMessage`]: crate::payloads::PinChatMessage
530 /// [`UnpinChatMessage`]: crate::payloads::UnpinChatMessage
531 NotEnoughRightsToManagePins = "Bad Request: not enough rights to manage pinned messages in the chat",
532
533 /// Occurs when bot tries change default chat permissions without "Ban
534 /// Users" permission in this chat.
535 ///
536 /// May happen in methods:
537 /// 1. [`SetChatPermissions`]
538 ///
539 /// [`SetChatPermissions`]: crate::payloads::SetChatPermissions
540 NotEnoughRightsToChangeChatPermissions = "Bad Request: not enough rights to change chat permissions",
541
542 /// Occurs when bot tries to use method in group which is allowed only in a
543 /// supergroup or channel.
544 MethodNotAvailableInPrivateChats = "Bad Request: method is available only for supergroups and channel",
545
546 /// Occurs when bot tries to demote chat creator.
547 ///
548 /// May happen in methods:
549 /// 1. [`PromoteChatMember`]
550 ///
551 /// [`PromoteChatMember`]: crate::payloads::PromoteChatMember
552 CantDemoteChatCreator = "Bad Request: can't demote chat creator",
553
554 /// Occurs when bot tries to restrict self in group chats.
555 ///
556 /// May happen in methods:
557 /// 1. [`RestrictChatMember`]
558 ///
559 /// [`RestrictChatMember`]: crate::payloads::RestrictChatMember
560 CantRestrictSelf = "Bad Request: can't restrict self",
561
562 /// Occurs when bot tries to restrict chat member without rights to
563 /// restrict in this chat.
564 ///
565 /// May happen in methods:
566 /// 1. [`RestrictChatMember`]
567 ///
568 /// [`RestrictChatMember`]: crate::payloads::RestrictChatMember
569 NotEnoughRightsToRestrict = "Bad Request: not enough rights to restrict/unrestrict chat member",
570
571 /// Occurs when bot tries to post a message in a channel without "Post
572 /// Messages" admin right.
573 NotEnoughRightsToPostMessages = "Bad Request: need administrator rights in the channel chat",
574
575 /// Occurs when bot tries set webhook to protocol other than HTTPS.
576 ///
577 /// May happen in methods:
578 /// 1. [`SetWebhook`]
579 ///
580 /// [`SetWebhook`]: crate::payloads::SetWebhook
581 WebhookRequireHttps = "Bad Request: bad webhook: HTTPS url must be provided for webhook",
582
583 /// Occurs when bot tries to set webhook to port other than 80, 88, 443 or
584 /// 8443.
585 ///
586 /// May happen in methods:
587 /// 1. [`SetWebhook`]
588 ///
589 /// [`SetWebhook`]: crate::payloads::SetWebhook
590 BadWebhookPort = "Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 or 8443",
591
592 /// Occurs when bot tries to set webhook to unknown host.
593 ///
594 /// May happen in methods:
595 /// 1. [`SetWebhook`]
596 ///
597 /// [`SetWebhook`]: crate::payloads::SetWebhook
598 UnknownHost = "Bad Request: bad webhook: Failed to resolve host: Name or service not known",
599
600 /// Occurs when bot tries to set webhook to invalid URL.
601 ///
602 /// May happen in methods:
603 /// 1. [`SetWebhook`]
604 ///
605 /// [`SetWebhook`]: crate::payloads::SetWebhook
606 CantParseUrl = "Bad Request: can't parse URL",
607
608 /// Occurs when bot tries to send message with unfinished entities.
609 ///
610 /// May happen in methods:
611 /// 1. [`SendMessage`]
612 ///
613 /// [`SendMessage`]: crate::payloads::SendMessage
614 CantParseEntities(String) = "{0}" with |text: &str| {
615 if text.starts_with("Bad Request: can't parse entities") {
616 Some(text.to_owned())
617 } else {
618 None
619 }
620 },
621
622 /// Occurs when bot tries to use getUpdates while webhook is active.
623 ///
624 /// May happen in methods:
625 /// 1. [`GetUpdates`]
626 ///
627 /// [`GetUpdates`]: crate::payloads::GetUpdates
628 CantGetUpdates = "can't use getUpdates method while webhook is active",
629
630 /// Occurs when bot tries to do some in group where bot was kicked.
631 ///
632 /// May happen in methods:
633 /// 1. [`SendMessage`]
634 ///
635 /// [`SendMessage`]: crate::payloads::SendMessage
636 BotKicked = "Unauthorized: bot was kicked from a chat",
637
638 /// Occurs when bot tries to do something in a supergroup the bot was
639 /// kicked from.
640 ///
641 /// May happen in methods:
642 /// 1. [`SendMessage`]
643 ///
644 /// [`SendMessage`]: crate::payloads::SendMessage
645 BotKickedFromSupergroup = "Forbidden: bot was kicked from the supergroup chat",
646
647 /// Occurs when bot tries to do something in a channel the bot was
648 /// kicked from.
649 ///
650 /// May happen in methods:
651 /// 1. [`SendMessage`]
652 ///
653 /// [`SendMessage`]: crate::payloads::SendMessage
654 BotKickedFromChannel = "Forbidden: bot was kicked from the channel chat",
655
656 /// Occurs when bot tries to send a message to a deactivated user (i.e. a
657 /// user that was banned by telegram).
658 ///
659 /// May happen in methods:
660 /// 1. [`SendMessage`]
661 ///
662 /// [`SendMessage`]: crate::payloads::SendMessage
663 UserDeactivated = "Forbidden: user is deactivated",
664
665 /// Occurs when you tries to initiate conversation with a user.
666 ///
667 /// May happen in methods:
668 /// 1. [`SendMessage`]
669 ///
670 /// [`SendMessage`]: crate::payloads::SendMessage
671 CantInitiateConversation = "Unauthorized: bot can't initiate conversation with a user",
672
673 /// Occurs when you tries to send message to bot.
674 ///
675 /// May happen in methods:
676 /// 1. [`SendMessage`]
677 ///
678 /// [`SendMessage`]: crate::payloads::SendMessage
679 CantTalkWithBots = "Unauthorized: bot can't send messages to bots",
680
681 /// Occurs when bot tries to send button with invalid http url.
682 ///
683 /// May happen in methods:
684 /// 1. [`SendMessage`]
685 ///
686 /// [`SendMessage`]: crate::payloads::SendMessage
687 WrongHttpUrl = "Bad Request: wrong HTTP URL",
688
689 /// Occurs when multiple [`GetUpdates`] calls happen at the same time.
690 ///
691 /// This can happen if
692 /// 1. You are running multiple bot instances (including on different servers/hosting platforms)
693 /// 2. You are running multiple update consumers (like `Dispatcher` or `repl`)
694 /// 3. You are calling [`GetUpdates`] yourself and the second call is done before the first one finishes
695 ///
696 /// May happen in methods:
697 /// 1. [`GetUpdates`]
698 ///
699 /// [`GetUpdates`]: crate::payloads::GetUpdates
700 TerminatedByOtherGetUpdates = "Conflict: terminated by other getUpdates request; make sure that only one bot instance \
701 is running",
702
703 /// Occurs when bot tries to get file by invalid file id.
704 ///
705 /// May happen in methods:
706 /// 1. [`GetFile`]
707 ///
708 /// [`GetFile`]: crate::payloads::GetFile
709 FileIdInvalid = "Bad Request: invalid file id",
710
711 /// Occurs when bot tries to upload a file which is larger than 50 MB using
712 /// multipart/form-data.
713 ///
714 /// May happen in methods:
715 /// 1. [`SendVideo`]
716 /// 2. [`SendDocument`]
717 ///
718 /// [`SendVideo`]: crate::payloads::SendVideo
719 /// [`SendDocument`]: crate::payloads::SendDocument
720 RequestEntityTooLarge = "Request Entity Too Large",
721
722
723 /// Error which is not known to `teloxide`.
724 ///
725 /// If you've received this error, please [open an issue] with the
726 /// description of the error.
727 ///
728 /// [open an issue]: https://github.com/teloxide/teloxide/issues/new
729 Unknown(String) = "Unknown error: {0:?}" with |text: &str| Some(text.to_owned())
730 }
731}
732
733/// This impl allows to use `?` to propagate [`DownloadError`]s in function
734/// returning [`RequestError`]s. For example:
735///
736/// ```rust
737/// # use teloxide_core::errors::{DownloadError, RequestError};
738///
739/// async fn handler() -> Result<(), RequestError> {
740/// download_file().await?; // `?` just works
741///
742/// Ok(())
743/// }
744///
745/// async fn download_file() -> Result<(), DownloadError> {
746/// /* download file here */
747/// Ok(())
748/// }
749/// ```
750impl From<DownloadError> for RequestError {
751 fn from(download_err: DownloadError) -> Self {
752 match download_err {
753 DownloadError::Network(err) => RequestError::Network(err),
754 DownloadError::Io(err) => RequestError::Io(err),
755 }
756 }
757}
758
759impl From<reqwest::Error> for DownloadError {
760 fn from(error: reqwest::Error) -> Self {
761 DownloadError::Network(Arc::new(hide_token(error)))
762 }
763}
764
765impl From<reqwest::Error> for RequestError {
766 fn from(error: reqwest::Error) -> Self {
767 RequestError::Network(Arc::new(hide_token(error)))
768 }
769}
770
771/// Replaces token in the url in the error with `token:redacted` string.
772pub(crate) fn hide_token(mut error: reqwest::Error) -> reqwest::Error {
773 let url = match error.url_mut() {
774 Some(url) => url,
775 None => return error,
776 };
777
778 if let Some(mut segments) = url.path_segments() {
779 // Usually the url looks like "bot<token>/..." or "file/bot<token>/...".
780 let (beginning, segment) = match segments.next() {
781 Some("file") => ("file/", segments.next()),
782 segment => ("", segment),
783 };
784
785 if let Some(token) = segment.and_then(|s| s.strip_prefix("bot")) {
786 // make sure that what we are about to delete looks like a bot token
787 if let Some((id, secret)) = token.split_once(':') {
788 // The part before the : in the token is the id of the bot.
789 let id_character = |c: char| c.is_ascii_digit();
790
791 // The part after the : in the token is the secret.
792 //
793 // In all bot tokens we could find the secret is 35 characters long and is
794 // 0-9a-zA-Z_- only.
795 //
796 // It would be nice to research if TBA always has 35 character secrets or if it
797 // is just a coincidence.
798 const SECRET_LENGTH: usize = 35;
799 let secret_character = |c: char| c.is_ascii_alphanumeric() || c == '-' || c == '_';
800
801 if secret.len() >= SECRET_LENGTH
802 && id.chars().all(id_character)
803 && secret.chars().all(secret_character)
804 {
805 // found token, hide only the token
806 let without_token =
807 &url.path()[(beginning.len() + "/bot".len() + token.len())..];
808 let redacted = format!("{beginning}token:redacted{without_token}");
809
810 url.set_path(&redacted);
811 return error;
812 }
813 }
814 }
815 }
816
817 // couldn't find token in the url, hide the whole url
818 error.without_url()
819}
820
821#[cfg(test)]
822mod tests {
823 #[test]
824 fn custom_result() {
825 use super::ApiError;
826 use serde::Deserialize;
827
828 let cases = &[
829 ("{\"data\": \"Forbidden: bot was blocked by the user\"}", ApiError::BotBlocked),
830 ("{\"data\": \"Unauthorized\"}", ApiError::InvalidToken),
831 ("{\"data\": \"Not Found\"}", ApiError::InvalidToken),
832 (
833 "{\"data\": \"Bad Request: message is not modified: specified new message content \
834 and reply markup are exactly the same as a current content and reply markup of \
835 the message\"}",
836 ApiError::MessageNotModified,
837 ),
838 ("{\"data\": \"Bad Request: MESSAGE_ID_INVALID\"}", ApiError::MessageIdInvalid),
839 (
840 "{\"data\": \"Bad Request: message to forward not found\"}",
841 ApiError::MessageToForwardNotFound,
842 ),
843 (
844 "{\"data\": \"Bad Request: message to delete not found\"}",
845 ApiError::MessageToDeleteNotFound,
846 ),
847 (
848 "{\"data\": \"Bad Request: message to copy not found\"}",
849 ApiError::MessageToCopyNotFound,
850 ),
851 ("{\"data\": \"Bad Request: message text is empty\"}", ApiError::MessageTextIsEmpty),
852 ("{\"data\": \"Bad Request: message can't be edited\"}", ApiError::MessageCantBeEdited),
853 (
854 "{\"data\": \"Bad Request: message can't be deleted\"}",
855 ApiError::MessageCantBeDeleted,
856 ),
857 (
858 "{\"data\": \"Bad Request: message to edit not found\"}",
859 ApiError::MessageToEditNotFound,
860 ),
861 (
862 "{\"data\": \"Bad Request: message to be replied not found\"}",
863 ApiError::MessageToReplyNotFound,
864 ),
865 (
866 "{\"data\": \"Bad Request: message identifier is not specified\"}",
867 ApiError::MessageIdentifierNotSpecified,
868 ),
869 ("{\"data\": \"Bad Request: message is too long\"}", ApiError::MessageIsTooLong),
870 ("{\"data\": \"Bad Request: MESSAGE_TOO_LONG\"}", ApiError::EditedMessageIsTooLong),
871 (
872 "{\"data\": \"Bad Request: Too much messages to send as an album\"}",
873 ApiError::TooMuchMessages,
874 ),
875 ("{\"data\": \"Bad Request: RESULTS_TOO_MUCH\"}", ApiError::TooMuchInlineQueryResults),
876 (
877 "{\"data\": \"Bad Request: poll has already been closed\"}",
878 ApiError::PollHasAlreadyClosed,
879 ),
880 (
881 "{\"data\": \"Bad Request: poll must have at least 2 option\"}",
882 ApiError::PollMustHaveMoreOptions,
883 ),
884 (
885 "{\"data\": \"Bad Request: poll can't have more than 12 options\"}",
886 ApiError::PollCantHaveMoreOptions,
887 ),
888 (
889 "{\"data\": \"Bad Request: poll options must be non-empty\"}",
890 ApiError::PollOptionsMustBeNonEmpty,
891 ),
892 (
893 "{\"data\": \"Bad Request: poll question must be non-empty\"}",
894 ApiError::PollQuestionMustBeNonEmpty,
895 ),
896 (
897 "{\"data\": \"Bad Request: poll options length must not exceed 100\"}",
898 ApiError::PollOptionsLengthTooLong,
899 ),
900 (
901 "{\"data\": \"Bad Request: poll question length must not exceed 255\"}",
902 ApiError::PollQuestionLengthTooLong,
903 ),
904 (
905 "{\"data\": \"Bad Request: message with poll to stop not found\"}",
906 ApiError::MessageWithPollNotFound,
907 ),
908 ("{\"data\": \"Bad Request: message is not a poll\"}", ApiError::MessageIsNotAPoll),
909 ("{\"data\": \"Bad Request: chat not found\"}", ApiError::ChatNotFound),
910 ("{\"data\": \"Bad Request: user not found\"}", ApiError::UserNotFound),
911 (
912 "{\"data\": \"Bad Request: chat description is not modified\"}",
913 ApiError::ChatDescriptionIsNotModified,
914 ),
915 (
916 "{\"data\": \"Bad Request: query is too old and response timeout expired or query \
917 id is invalid\"}",
918 ApiError::InvalidQueryId,
919 ),
920 ("{\"data\": \"Bad Request: BUTTON_URL_INVALID\"}", ApiError::ButtonUrlInvalid),
921 ("{\"data\": \"Bad Request: BUTTON_DATA_INVALID\"}", ApiError::ButtonDataInvalid),
922 (
923 "{\"data\": \"Bad Request: can't parse inline keyboard button: Text buttons are \
924 unallowed in the inline keyboard\"}",
925 ApiError::TextButtonsAreUnallowed,
926 ),
927 ("{\"data\": \"Bad Request: wrong file id\"}", ApiError::WrongFileId),
928 (
929 "{\"data\": \"Bad Request: wrong file identifier/HTTP URL specified\"}",
930 ApiError::WrongFileIdOrUrl,
931 ),
932 (
933 "{\"data\": \"Bad Request: failed to get HTTP URL content\"}",
934 ApiError::FailedToGetUrlContent,
935 ),
936 ("{\"data\": \"Bad Request: group is deactivated\"}", ApiError::GroupDeactivated),
937 ("{\"data\": \"Bad Request: IMAGE_PROCESS_FAILED\"}", ApiError::ImageProcessFailed),
938 (
939 "{\"data\": \"Bad Request: Photo should be uploaded as an InputFile\"}",
940 ApiError::PhotoAsInputFileRequired,
941 ),
942 ("{\"data\": \"Bad Request: STICKERSET_INVALID\"}", ApiError::InvalidStickersSet),
943 (
944 "{\"data\": \"Bad Request: sticker set name is already occupied\"}",
945 ApiError::StickerSetNameOccupied,
946 ),
947 ("{\"data\": \"Bad Request: USER_IS_BOT\"}", ApiError::StickerSetOwnerIsBot),
948 (
949 "{\"data\": \"Bad Request: invalid sticker set name is specified\"}",
950 ApiError::InvalidStickerName,
951 ),
952 (
953 "{\"data\": \"Bad Request: not enough rights to pin a message\"}",
954 ApiError::NotEnoughRightsToPinMessage,
955 ),
956 (
957 "{\"data\": \"Bad Request: not enough rights to manage pinned messages in the \
958 chat\"}",
959 ApiError::NotEnoughRightsToManagePins,
960 ),
961 (
962 "{\"data\": \"Bad Request: not enough rights to change chat permissions\"}",
963 ApiError::NotEnoughRightsToChangeChatPermissions,
964 ),
965 (
966 "{\"data\": \"Bad Request: method is available only for supergroups and channel\"}",
967 ApiError::MethodNotAvailableInPrivateChats,
968 ),
969 (
970 "{\"data\": \"Bad Request: can't demote chat creator\"}",
971 ApiError::CantDemoteChatCreator,
972 ),
973 ("{\"data\": \"Bad Request: can't restrict self\"}", ApiError::CantRestrictSelf),
974 (
975 "{\"data\": \"Bad Request: not enough rights to restrict/unrestrict chat member\"}",
976 ApiError::NotEnoughRightsToRestrict,
977 ),
978 (
979 "{\"data\": \"Bad Request: need administrator rights in the channel chat\"}",
980 ApiError::NotEnoughRightsToPostMessages,
981 ),
982 (
983 "{\"data\": \"Bad Request: bad webhook: HTTPS url must be provided for webhook\"}",
984 ApiError::WebhookRequireHttps,
985 ),
986 (
987 "{\"data\": \"Bad Request: bad webhook: Webhook can be set up only on ports 80, \
988 88, 443 or 8443\"}",
989 ApiError::BadWebhookPort,
990 ),
991 (
992 "{\"data\": \"Bad Request: bad webhook: Failed to resolve host: Name or service \
993 not known\"}",
994 ApiError::UnknownHost,
995 ),
996 ("{\"data\": \"Bad Request: can't parse URL\"}", ApiError::CantParseUrl),
997 (
998 "{\"data\": \"Bad Request: can't parse entities: SomeRandomString\"}",
999 ApiError::CantParseEntities(
1000 "Bad Request: can't parse entities: SomeRandomString".to_owned(),
1001 ),
1002 ),
1003 (
1004 "{\"data\": \"can't use getUpdates method while webhook is active\"}",
1005 ApiError::CantGetUpdates,
1006 ),
1007 ("{\"data\": \"Unauthorized: bot was kicked from a chat\"}", ApiError::BotKicked),
1008 (
1009 "{\"data\": \"Forbidden: bot was kicked from the supergroup chat\"}",
1010 ApiError::BotKickedFromSupergroup,
1011 ),
1012 ("{\"data\": \"Forbidden: user is deactivated\"}", ApiError::UserDeactivated),
1013 (
1014 "{\"data\": \"Unauthorized: bot can't initiate conversation with a user\"}",
1015 ApiError::CantInitiateConversation,
1016 ),
1017 (
1018 "{\"data\": \"Unauthorized: bot can't send messages to bots\"}",
1019 ApiError::CantTalkWithBots,
1020 ),
1021 ("{\"data\": \"Bad Request: wrong HTTP URL\"}", ApiError::WrongHttpUrl),
1022 (
1023 "{\"data\": \"Conflict: terminated by other getUpdates request; make sure that \
1024 only one bot instance is running\"}",
1025 ApiError::TerminatedByOtherGetUpdates,
1026 ),
1027 ("{\"data\": \"Bad Request: invalid file id\"}", ApiError::FileIdInvalid),
1028 ("{\"data\": \"Request Entity Too Large\"}", ApiError::RequestEntityTooLarge),
1029 ("{\"data\": \"RandomError\"}", ApiError::Unknown("RandomError".to_string())),
1030 ];
1031
1032 #[derive(Deserialize, Debug)]
1033 struct Res<T> {
1034 data: T,
1035 }
1036
1037 for (data, expected) in cases {
1038 let raw = serde_json::from_str::<Res<String>>(data).unwrap().data;
1039 let parsed = serde_json::from_str::<Res<ApiError>>(data).unwrap().data;
1040 assert_eq!(&parsed, expected);
1041
1042 let expected_error_message = match parsed {
1043 ApiError::Unknown(_) => {
1044 format!("Unknown error: \"{raw}\"")
1045 }
1046 ApiError::InvalidToken => "Invalid bot token".to_owned(),
1047 _ => raw,
1048 };
1049 assert_eq!(parsed.to_string(), expected_error_message);
1050 }
1051 }
1052}