fedimint_connectors/
error.rs

1use fedimint_core::PeerId;
2use fedimint_core::util::SafeUrl;
3use fedimint_logging::LOG_CLIENT_NET_API;
4use thiserror::Error;
5use tracing::{trace, warn};
6
7/// An API request error when calling a single federation peer
8#[derive(Debug, Error)]
9#[non_exhaustive]
10pub enum ServerError {
11    /// The response payload was returned successfully but failed to be
12    /// deserialized
13    #[error("Response deserialization error: {0}")]
14    ResponseDeserialization(anyhow::Error),
15
16    /// The request was addressed to an invalid `peer_id`
17    #[error("Invalid peer id: {peer_id}")]
18    InvalidPeerId { peer_id: PeerId },
19
20    /// The request was addressed to an invalid `url`
21    #[error("Invalid peer url: {url}")]
22    InvalidPeerUrl { url: SafeUrl, source: anyhow::Error },
23
24    /// The endpoint specification for the peer is invalid (e.g. wrong url)
25    #[error("Invalid endpoint")]
26    InvalidEndpoint(anyhow::Error),
27
28    /// Could not connect
29    #[error("Connection failed: {0}")]
30    Connection(anyhow::Error),
31
32    /// Underlying transport failed, in some typical way
33    #[error("Transport error: {0}")]
34    Transport(anyhow::Error),
35
36    /// The rpc id (e.g. jsonrpc method name) was not recognized by the peer
37    ///
38    /// This one is important and sometimes used to detect backward
39    /// compatibility capabilities, so transports should properly support
40    /// it.
41    #[error("Invalid rpc id")]
42    InvalidRpcId(anyhow::Error),
43
44    /// Something about the request we've sent was wrong, should not typically
45    /// happen
46    #[error("Invalid request")]
47    InvalidRequest(anyhow::Error),
48
49    /// Something about the response was wrong, should not typically happen
50    #[error("Invalid response: {0}")]
51    InvalidResponse(anyhow::Error),
52
53    /// Server returned an internal error, suggesting something is wrong with it
54    #[error("Unspecified server error: {0}")]
55    ServerError(anyhow::Error),
56
57    /// Some condition on the response this not match
58    ///
59    /// Typically expected, and often used in `FilterMap` query strategy to
60    /// reject responses that don't match some criteria.
61    #[error("Unspecified condition error: {0}")]
62    ConditionFailed(anyhow::Error),
63
64    /// An internal client error
65    ///
66    /// Things that shouldn't happen (better than panicking), logical errors,
67    /// malfunctions caused by internal issues.
68    #[error("Unspecified internal client error: {0}")]
69    InternalClientError(anyhow::Error),
70}
71
72impl ServerError {
73    pub fn is_unusual(&self) -> bool {
74        match self {
75            ServerError::ResponseDeserialization(_)
76            | ServerError::InvalidPeerId { .. }
77            | ServerError::InvalidPeerUrl { .. }
78            | ServerError::InvalidResponse(_)
79            | ServerError::InvalidRpcId(_)
80            | ServerError::InvalidRequest(_)
81            | ServerError::InternalClientError(_)
82            | ServerError::InvalidEndpoint(_)
83            | ServerError::ServerError(_) => true,
84            ServerError::Connection(_)
85            | ServerError::Transport(_)
86            | ServerError::ConditionFailed(_) => false,
87        }
88    }
89    /// Report errors that are worth reporting
90    ///
91    /// The goal here is to avoid spamming logs with errors that happen commonly
92    /// for all sorts of expected reasons, while printing ones that suggest
93    /// there's a problem.
94    pub fn report_if_unusual(&self, peer_id: PeerId, context: &str) {
95        let unusual = self.is_unusual();
96
97        trace!(target: LOG_CLIENT_NET_API, error = %self, %context, "ServerError");
98
99        if unusual {
100            warn!(target: LOG_CLIENT_NET_API, error = %self,%context, %peer_id, "Unusual ServerError");
101        }
102    }
103}