Skip to main content

derec_library/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 DeRec Alliance. All rights reserved.
3
4//! # Error Types
5//!
6//! This module defines the top-level error type returned by the public APIs
7//! of the library.
8//!
9//! ## Error model
10//!
11//! The crate exposes a unified [`Error`] type that wraps the error types
12//! produced by each protocol flow:
13//!
14//! - pairing
15//! - sharing
16//! - verification
17//! - recovery
18//! - discovery
19//!
20//! Each flow defines its own specialized error type, which is converted into
21//! [`Error`] using `From` conversions. This allows public functions to return
22//! `Result<T, crate::Error>` while still preserving the precise cause of the
23//! failure.
24//!
25//! Applications consuming this library may pattern match on the [`Error`]
26//! variants to determine which protocol phase produced the error.
27//!
28//! ## Non-exhaustive
29//!
30//! The [`Error`] enum is marked `#[non_exhaustive]`, meaning additional variants
31//! may be introduced in future versions without breaking API compatibility.
32//! Consumers should include a fallback match arm when matching on this type.
33//!
34//! ## Internal invariants
35//!
36//! Some variants represent violations of internal invariants. These indicate
37//! logic errors or unexpected states and should normally not occur in correct
38//! usage of the library.
39
40#[derive(Debug, thiserror::Error)]
41#[non_exhaustive]
42pub enum Error {
43    #[error(transparent)]
44    Pairing(#[from] crate::primitives::pairing::PairingError),
45
46    #[error(transparent)]
47    Recovery(#[from] crate::primitives::recovery::RecoveryError),
48
49    #[error(transparent)]
50    Discovery(#[from] crate::primitives::discovery::DiscoveryError),
51
52    #[error(transparent)]
53    Sharing(#[from] crate::primitives::sharing::SharingError),
54
55    #[error(transparent)]
56    Verification(#[from] crate::primitives::verification::VerificationError),
57
58    #[error(transparent)]
59    Unpairing(#[from] crate::primitives::unpairing::UnpairingError),
60
61    #[error(transparent)]
62    Restore(#[from] crate::protocol::RestoreError),
63
64    #[error(transparent)]
65    DeRecMessage(#[from] crate::derec_message::DeRecMessageBuilderError),
66
67    #[error(transparent)]
68    SecretStore(#[from] crate::protocol::SecretStoreError),
69
70    #[error(transparent)]
71    ChannelStore(#[from] crate::protocol::ChannelStoreError),
72
73    #[error(transparent)]
74    ShareStore(#[from] crate::protocol::ShareStoreError),
75
76    #[error(transparent)]
77    StateStore(#[from] crate::protocol::StateStoreError),
78
79    #[error(transparent)]
80    Transport(#[from] crate::transport::TransportValidationError),
81
82    #[error("invalid input: {0}")]
83    InvalidInput(&'static str),
84
85    #[error("protobuf decode error")]
86    ProtobufDecode(#[source] prost::DecodeError),
87
88    #[error("protobuf encode error")]
89    ProtobufEncode(#[source] prost::EncodeError),
90
91    #[error("internal invariant violated: {0}")]
92    Invariant(&'static str),
93
94    #[error(
95        "role mismatch on channel {channel_id:?}: expected {expected:?}, got {actual:?}"
96    )]
97    RoleMismatch {
98        channel_id: crate::types::ChannelId,
99        expected: derec_proto::SenderKind,
100        actual: derec_proto::SenderKind,
101    },
102
103    /// `start(Pairing)` was called for a `channel_id` that already has
104    /// a `Paired` channel record. Re-pairing would silently overwrite
105    /// the stored `SharedKey`; the caller must skip the call or unpair
106    /// first.
107    #[error("channel {channel_id:?} is already paired")]
108    ChannelAlreadyPaired { channel_id: crate::types::ChannelId },
109
110    /// A replica-mode flow was attempted but the protocol was built without
111    /// [`DeRecProtocolBuilder::with_replica_id`](crate::protocol::DeRecProtocolBuilder::with_replica_id).
112    /// Surfaces at the entry points of every flow that requires a local
113    /// replica identity (initiating a replica-mode pairing, handling an
114    /// inbound `PairRequest` whose `sender_kind` is `ReplicaSource` or
115    /// `ReplicaDestination`, etc.).
116    #[error("replica id not configured: build the protocol with .with_replica_id(..) to enable replica flows")]
117    ReplicaIdNotConfigured,
118}
119
120impl Error {
121    pub fn as_non_ok_status(&self) -> Option<(i32, &str)> {
122        match self {
123            Error::Pairing(crate::primitives::pairing::PairingError::NonOkStatus {
124                status,
125                memo,
126            }) => Some((*status, memo)),
127            Error::Sharing(crate::primitives::sharing::SharingError::NonOkStatus {
128                status,
129                memo,
130            }) => Some((*status, memo)),
131            Error::Verification(
132                crate::primitives::verification::VerificationError::NonOkStatus { status, memo },
133            ) => Some((*status, memo)),
134            Error::Discovery(crate::primitives::discovery::DiscoveryError::NonOkStatus {
135                status,
136                memo,
137            }) => Some((*status, memo)),
138            Error::Recovery(crate::primitives::recovery::RecoveryError::NonOkStatus {
139                status,
140                memo,
141            }) => Some((*status, memo)),
142            Error::Unpairing(crate::primitives::unpairing::UnpairingError::NonOkStatus {
143                status,
144                memo,
145            }) => Some((*status, memo)),
146            _ => None,
147        }
148    }
149}