atproto_identity/errors.rs
1//! # Structured Error Types
2//!
3//! Comprehensive error handling for AT Protocol identity operations using structured error types
4//! with the `thiserror` library. All errors follow the project convention of prefixed error codes
5//! with descriptive messages.
6//!
7//! ## Error Categories
8//!
9//! - **`WebDIDError`** (web-1 to web-4): Errors specific to `did:web` operations including URL conversion and document fetching
10//! - **`ConfigError`** (config-1 to config-3): Configuration and environment variable related errors
11//! - **`ResolveError`** (resolve-1 to resolve-8): Handle and DID resolution errors including DNS/HTTP failures and conflicts
12//! - **`PLCDIDError`** (plc-1 to plc-2): PLC directory communication and document parsing errors
13//! - **`KeyError`** (key-1 to key-12): Cryptographic key operations including generation, parsing, signing, and validation
14//! - **`StorageError`** (storage-1 to storage-3): Storage operations including cache lock failures and data access errors
15//!
16//! ## Error Format
17//!
18//! All errors use the standardized format: `error-atproto-identity-{domain}-{number} {message}: {details}`
19
20use thiserror::Error;
21
22/// Error types that can occur when working with Web DIDs
23#[derive(Debug, Error)]
24pub enum WebDIDError {
25 /// Occurs when the DID is missing the 'did:web:' prefix
26 #[error("error-atproto-identity-web-1 Invalid DID format: missing 'did:web:' prefix")]
27 InvalidDIDPrefix,
28
29 /// Occurs when the DID is missing a hostname component
30 #[error("error-atproto-identity-web-2 Invalid DID format: missing hostname component")]
31 MissingHostname,
32
33 /// Occurs when the HTTP request to fetch the DID document fails
34 #[error("error-atproto-identity-web-3 HTTP request failed: {url} {error}")]
35 HttpRequestFailed {
36 /// The URL that was requested
37 url: String,
38 /// The underlying HTTP error
39 error: reqwest::Error,
40 },
41
42 /// Occurs when the DID document cannot be parsed from the HTTP response
43 #[error("error-atproto-identity-web-4 Failed to parse DID document: {url} {error}")]
44 DocumentParseFailed {
45 /// The URL that was requested
46 url: String,
47 /// The underlying parse error
48 error: reqwest::Error,
49 },
50}
51
52/// Error types that can occur when working with configuration
53#[derive(Debug, Error)]
54pub enum ConfigError {
55 /// Occurs when a required environment variable is not set
56 #[error("error-atproto-identity-config-1 Required environment variable not found: {name}")]
57 MissingEnvironmentVariable {
58 /// The name of the missing environment variable
59 name: String,
60 },
61
62 /// Occurs when parsing an IP address from nameserver configuration fails
63 #[error("error-atproto-identity-config-2 Unable to parse nameserver IP: {value}")]
64 InvalidNameserverIP {
65 /// The invalid IP address value that could not be parsed
66 value: String,
67 },
68
69 /// Occurs when version information cannot be determined
70 #[error(
71 "error-atproto-identity-config-3 Version information not available: GIT_HASH or CARGO_PKG_VERSION must be set"
72 )]
73 VersionNotAvailable,
74}
75
76/// Error types that can occur when resolving AT Protocol identities
77#[derive(Debug, Error)]
78pub enum ResolveError {
79 /// Occurs when multiple different DIDs are found via DNS TXT record lookup
80 #[error(
81 "error-atproto-identity-resolve-1 Multiple DIDs resolved for handle: expected single DID"
82 )]
83 MultipleDIDsFound,
84
85 /// Occurs when no DIDs are found via either DNS or HTTP resolution methods
86 #[error(
87 "error-atproto-identity-resolve-2 No DIDs resolved for handle: no resolution methods succeeded"
88 )]
89 NoDIDsFound,
90
91 /// Occurs when DNS and HTTP resolution return different DIDs for the same handle
92 #[error(
93 "error-atproto-identity-resolve-3 Conflicting DIDs found for handle: DNS and HTTP resolution returned different results"
94 )]
95 ConflictingDIDsFound,
96
97 /// Occurs when DNS TXT record lookup fails
98 #[error("error-atproto-identity-resolve-4 DNS resolution failed: {error:?}")]
99 DNSResolutionFailed {
100 /// The underlying DNS resolution error
101 error: hickory_resolver::ResolveError,
102 },
103
104 /// Occurs when HTTP request to .well-known/atproto-did endpoint fails
105 #[error("error-atproto-identity-resolve-5 HTTP resolution failed: {error:?}")]
106 HTTPResolutionFailed {
107 /// The underlying HTTP error
108 error: reqwest::Error,
109 },
110
111 /// Occurs when HTTP response from .well-known/atproto-did doesn't start with "did:"
112 #[error(
113 "error-atproto-identity-resolve-6 Invalid HTTP resolution response: expected DID format"
114 )]
115 InvalidHTTPResolutionResponse,
116
117 /// Occurs when input cannot be parsed as a valid handle or DID
118 #[error("error-atproto-identity-resolve-7 Invalid input format: expected valid handle or DID")]
119 InvalidInput,
120
121 /// Occurs when subject resolution results in a handle instead of expected DID
122 #[error("error-atproto-identity-resolve-8 Subject resolved to handle instead of DID")]
123 SubjectResolvedToHandle,
124}
125
126/// Error types that can occur when working with PLC DIDs
127#[derive(Debug, Error)]
128pub enum PLCDIDError {
129 /// Occurs when the HTTP request to the PLC directory fails
130 #[error("error-atproto-identity-plc-1 HTTP request failed: {url} {error}")]
131 HttpRequestFailed {
132 /// The URL that was requested
133 url: String,
134 /// The underlying HTTP error
135 error: reqwest::Error,
136 },
137
138 /// Occurs when the DID document cannot be parsed from the PLC directory response
139 #[error("error-atproto-identity-plc-2 Failed to parse DID document: {url} {error}")]
140 DocumentParseFailed {
141 /// The URL that was requested
142 url: String,
143 /// The underlying parse error
144 error: reqwest::Error,
145 },
146}
147
148/// Error types that can occur when working with cryptographic keys
149#[derive(Debug, Error)]
150pub enum KeyError {
151 /// Occurs when multibase decoding of a key fails
152 #[error("error-atproto-identity-key-1 Error decoding key: {error:?}")]
153 DecodeError {
154 /// The underlying multibase decode error
155 error: multibase::Error,
156 },
157
158 /// Occurs when ECDSA signature parsing fails
159 #[error("error-atproto-identity-key-2 Signature parsing failed: {error:?}")]
160 SignatureError {
161 /// The underlying signature parsing error
162 error: ecdsa::signature::Error,
163 },
164
165 /// Occurs when P-256 key operations fail
166 #[error("error-atproto-identity-key-3 P-256 key operation failed: {error:?}")]
167 P256Error {
168 /// The underlying P-256 key error
169 error: p256::ecdsa::Error,
170 },
171
172 /// Occurs when P-384 key operations fail
173 #[error("error-atproto-identity-key-4 P-384 key operation failed: {error:?}")]
174 P384Error {
175 /// The underlying P-384 key error
176 error: p384::ecdsa::Error,
177 },
178
179 /// Occurs when K-256 key operations fail
180 #[error("error-atproto-identity-key-5 K-256 key operation failed: {error:?}")]
181 K256Error {
182 /// The underlying K-256 key error
183 error: k256::ecdsa::Error,
184 },
185
186 /// Occurs when ECDSA cryptographic operations fail
187 #[error("error-atproto-identity-key-6 ECDSA operation failed: {error:?}")]
188 ECDSAError {
189 /// The underlying ECDSA error
190 error: ecdsa::Error,
191 },
192
193 /// Occurs when secret key parsing or operations fail
194 #[error("error-atproto-identity-key-7 Secret key operation failed: {error:?}")]
195 SecretKeyError {
196 /// The underlying secret key error
197 error: ecdsa::elliptic_curve::Error,
198 },
199
200 /// Occurs when attempting to sign content with a public key instead of a private key
201 #[error("error-atproto-identity-key-8 Private key required for signature")]
202 PrivateKeyRequiredForSignature,
203
204 /// Occurs when attempting to generate a public key directly
205 #[error(
206 "error-atproto-identity-key-9 Public key generation not supported: generate private key instead"
207 )]
208 PublicKeyGenerationNotSupported,
209
210 /// Occurs when the decoded key data is too short to identify the key type
211 #[error("error-atproto-identity-key-10 Unidentified key type: key data too short")]
212 UnidentifiedKeyType,
213
214 /// Occurs when the multibase key type prefix is not recognized
215 #[error("error-atproto-identity-key-11 Invalid multibase key type: {prefix:?}")]
216 InvalidMultibaseKeyType {
217 /// The unrecognized key type prefix
218 prefix: Vec<u8>,
219 },
220
221 /// Occurs when JWK format conversion fails for supported key types
222 #[error("error-atproto-identity-key-12 JWK format conversion failed: {error}")]
223 JWKConversionFailed {
224 /// The underlying conversion error
225 error: String,
226 },
227}
228
229/// Error types that can occur when working with storage operations
230#[derive(Debug, Error)]
231pub enum StorageError {
232 /// Occurs when cache lock acquisition fails during document retrieval operations
233 #[error(
234 "error-atproto-identity-storage-1 Cache lock acquisition failed for get operation: {details}"
235 )]
236 CacheLockFailedGet {
237 /// Details about the lock failure
238 details: String,
239 },
240
241 /// Occurs when cache lock acquisition fails during document storage operations
242 #[error(
243 "error-atproto-identity-storage-2 Cache lock acquisition failed for store operation: {details}"
244 )]
245 CacheLockFailedStore {
246 /// Details about the lock failure
247 details: String,
248 },
249
250 /// Occurs when cache lock acquisition fails during document deletion operations
251 #[error(
252 "error-atproto-identity-storage-3 Cache lock acquisition failed for delete operation: {details}"
253 )]
254 CacheLockFailedDelete {
255 /// Details about the lock failure
256 details: String,
257 },
258}