Skip to main content

bitwarden_ipc/
error.rs

1use bitwarden_error::bitwarden_error;
2use thiserror::Error;
3
4use crate::rpc::error::RpcError;
5
6/// Classifies an IPC error as either fatal or recoverable.
7///
8/// The IPC client runs a single long-lived processing loop that is shared across every peer and
9/// every message. Historically *any* transport or crypto error tore that loop down, which meant a
10/// single transient failure (a handshake timeout, a peer disconnecting mid-send, a malformed
11/// frame) permanently disabled the shared client and it never recovered.
12///
13/// This trait lets each layer classify its own errors so the client can distinguish the two cases:
14/// - **Fatal** (`is_fatal() == true`): the client can no longer make progress, so the processing
15///   loop should stop.
16/// - **Recoverable** (`is_fatal() == false`): only the current operation failed, so the client
17///   should stay running and continue processing other messages.
18///
19/// Implementations should classify errors at construction, where the most context is available,
20/// and default ambiguous cases to recoverable. Failing open keeps the shared client alive, which
21/// is almost always the safer choice.
22pub trait IpcErrorKind {
23    /// Returns `true` if the error is fatal and the IPC client should stop processing messages, or
24    /// `false` if the error is recoverable and the client should keep running.
25    fn is_fatal(&self) -> bool;
26}
27
28impl IpcErrorKind for std::convert::Infallible {
29    fn is_fatal(&self) -> bool {
30        // `Infallible` can never be constructed, so this is unreachable.
31        match *self {}
32    }
33}
34
35#[cfg(any(test, feature = "test-support"))]
36impl IpcErrorKind for () {
37    fn is_fatal(&self) -> bool {
38        false
39    }
40}
41
42/// Error returned by [`IpcClient::start`](crate::IpcClient::start). Indicates that the IPC client
43/// is already running.
44#[derive(Debug, Error, Clone, PartialEq, Eq)]
45#[error("IPC client is already running")]
46#[bitwarden_error(basic)]
47pub struct AlreadyRunningError;
48
49/// Error returned by [`IpcClient::send`](crate::IpcClient::send). Wraps the underlying transport
50/// error as a string.
51#[derive(Debug, Error, Clone, PartialEq, Eq)]
52#[error("{0}")]
53pub struct SendError(pub(crate) String);
54
55#[derive(Debug, Error, Clone, PartialEq, Eq)]
56#[bitwarden_error(flat)]
57#[allow(missing_docs)]
58pub enum SubscribeError {
59    #[error("The IPC processing thread is not running")]
60    NotStarted,
61}
62
63#[derive(Debug, Error, PartialEq, Eq)]
64#[bitwarden_error(flat)]
65#[allow(missing_docs)]
66pub enum ReceiveError {
67    #[error("Failed to subscribe to the IPC channel: {0}")]
68    Channel(#[from] tokio::sync::broadcast::error::RecvError),
69
70    #[error("Timed out while waiting for a message: {0}")]
71    Timeout(#[from] bitwarden_threading::time::ElapsedError),
72
73    #[error("Cancelled while waiting for a message")]
74    Cancelled,
75}
76
77#[derive(Debug, Error, PartialEq, Eq)]
78#[bitwarden_error(flat)]
79#[allow(missing_docs)]
80pub enum TypedReceiveError {
81    #[error("Failed to subscribe to the IPC channel: {0}")]
82    Channel(#[from] tokio::sync::broadcast::error::RecvError),
83
84    #[error("Timed out while waiting for a message: {0}")]
85    Timeout(#[from] bitwarden_threading::time::ElapsedError),
86
87    #[error("Cancelled while waiting for a message")]
88    Cancelled,
89
90    #[error("Typing error: {0}")]
91    Typing(String),
92}
93
94impl From<ReceiveError> for TypedReceiveError {
95    fn from(value: ReceiveError) -> Self {
96        match value {
97            ReceiveError::Channel(e) => TypedReceiveError::Channel(e),
98            ReceiveError::Timeout(e) => TypedReceiveError::Timeout(e),
99            ReceiveError::Cancelled => TypedReceiveError::Cancelled,
100        }
101    }
102}
103
104#[derive(Debug, Error, PartialEq, Eq)]
105#[bitwarden_error(flat)]
106#[allow(missing_docs)]
107pub enum RequestError {
108    #[error(transparent)]
109    Subscribe(#[from] SubscribeError),
110
111    #[error(transparent)]
112    Receive(#[from] TypedReceiveError),
113
114    #[error("Timed out while waiting for a message: {0}")]
115    Timeout(#[from] bitwarden_threading::time::ElapsedError),
116
117    #[error("Failed to send message: {0}")]
118    Send(String),
119
120    #[error("Error occurred on the remote target: {0}")]
121    Rpc(#[from] RpcError),
122}