Skip to main content

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/// A failure to build a connector, or to reach a gateway through one.
8///
9/// Peer (guardian) requests report [`ServerError`] instead; this type covers
10/// the connector layer underneath it.
11#[derive(Debug, Error)]
12#[non_exhaustive]
13pub enum ConnectorError {
14    /// No connector is registered for the url's scheme.
15    #[error("Unsupported scheme {scheme}; missing endpoint handler")]
16    UnsupportedScheme { scheme: String },
17
18    /// The connector for this scheme was switched off when the registry was
19    /// built.
20    #[error("The {scheme} connector is not enabled")]
21    NotEnabled { scheme: &'static str },
22
23    /// Tor routing was requested from a build that has no Tor support.
24    #[error("Tor was requested, but support for it is not compiled in")]
25    TorNotCompiledIn,
26
27    /// This transport can reach guardians but not gateways.
28    #[error("This transport cannot connect to a gateway")]
29    GatewayUnsupported,
30
31    /// The url carries no host to address.
32    #[error("Missing host in url {url}")]
33    MissingHost { url: SafeUrl },
34
35    /// The url's host is not a valid Iroh node id.
36    #[error("Invalid Iroh node id {host}")]
37    InvalidNodeId {
38        host: String,
39        #[source]
40        source: Box<dyn std::error::Error + Send + Sync>,
41    },
42
43    /// The url could not be parsed.
44    #[error("Invalid url {url}")]
45    InvalidUrl {
46        url: String,
47        #[source]
48        source: Box<dyn std::error::Error + Send + Sync>,
49    },
50
51    /// The Iroh api url path selects a wire version this build does not know.
52    #[error("Unsupported Iroh api url path {path}")]
53    UnsupportedUrlPath { path: String },
54
55    /// The transport refused the connection or failed to start.
56    #[error("Transport failure")]
57    Transport(#[source] Box<dyn std::error::Error + Send + Sync>),
58
59    /// The Tor client could not be bootstrapped.
60    #[error("Failed to bootstrap the Tor client")]
61    Tor(#[source] Box<dyn std::error::Error + Send + Sync>),
62}
63
64/// An API request error when calling a single federation peer
65#[derive(Debug, Error)]
66#[non_exhaustive]
67pub enum ServerError {
68    /// The response payload was returned successfully but failed to be
69    /// deserialized
70    #[error("Response deserialization error: {0}")]
71    ResponseDeserialization(Box<dyn std::error::Error + Send + Sync>),
72
73    /// The request was addressed to an invalid `peer_id`
74    #[error("Invalid peer id: {peer_id}")]
75    InvalidPeerId { peer_id: PeerId },
76
77    /// The request was addressed to an invalid `url`
78    ///
79    /// The cause is boxed to keep `ServerError` cheap to move.
80    #[error("Invalid peer url: {url}")]
81    InvalidPeerUrl {
82        url: SafeUrl,
83        source: Box<ConnectorError>,
84    },
85
86    /// The endpoint specification for the peer is invalid (e.g. wrong url)
87    #[error("Invalid endpoint: {0}")]
88    InvalidEndpoint(Box<dyn std::error::Error + Send + Sync>),
89
90    /// Could not connect
91    #[error("Connection failed: {0}")]
92    Connection(Box<dyn std::error::Error + Send + Sync>),
93
94    /// Underlying transport failed, in some typical way
95    #[error("Transport error: {0}")]
96    Transport(Box<dyn std::error::Error + Send + Sync>),
97
98    /// The rpc id (e.g. jsonrpc method name) was not recognized by the peer
99    ///
100    /// This one is important and sometimes used to detect backward
101    /// compatibility capabilities, so transports should properly support
102    /// it.
103    #[error("Invalid rpc id: {0}")]
104    InvalidRpcId(String),
105
106    /// Something about the request we've sent was wrong, should not typically
107    /// happen
108    #[error("Invalid request: {0}")]
109    InvalidRequest(String),
110
111    /// Something about the response was wrong, should not typically happen
112    #[error("Invalid response: {0}")]
113    InvalidResponse(String),
114
115    /// Server returned an internal error, suggesting something is wrong with it
116    #[error("Unspecified server error: {0}")]
117    ServerError(String),
118
119    /// Some condition on the response this not match
120    ///
121    /// Typically expected, and often used in `FilterMap` query strategy to
122    /// reject responses that don't match some criteria.
123    #[error("Unspecified condition error: {0}")]
124    ConditionFailed(String),
125
126    /// An internal client error
127    ///
128    /// Things that shouldn't happen (better than panicking), logical errors,
129    /// malfunctions caused by internal issues.
130    #[error("Unspecified internal client error: {0}")]
131    InternalClientError(String),
132}
133
134impl ServerError {
135    pub fn is_unusual(&self) -> bool {
136        match self {
137            ServerError::ResponseDeserialization(_)
138            | ServerError::InvalidPeerId { .. }
139            | ServerError::InvalidPeerUrl { .. }
140            | ServerError::InvalidResponse(_)
141            | ServerError::InvalidRpcId(_)
142            | ServerError::InvalidRequest(_)
143            | ServerError::InternalClientError(_)
144            | ServerError::InvalidEndpoint(_)
145            | ServerError::ServerError(_) => true,
146            ServerError::Connection(_)
147            | ServerError::Transport(_)
148            | ServerError::ConditionFailed(_) => false,
149        }
150    }
151    /// Report errors that are worth reporting
152    ///
153    /// The goal here is to avoid spamming logs with errors that happen commonly
154    /// for all sorts of expected reasons, while printing ones that suggest
155    /// there's a problem.
156    pub fn report_if_unusual(&self, peer_id: PeerId, context: &str) {
157        let unusual = self.is_unusual();
158
159        trace!(target: LOG_CLIENT_NET_API, error = %self, %context, "ServerError");
160
161        if unusual {
162            warn!(target: LOG_CLIENT_NET_API, error = %self,%context, %peer_id, "Unusual ServerError");
163        }
164    }
165}