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 #[cfg(feature = "hickory-dns")]
99 #[error("error-atproto-identity-resolve-4 DNS resolution failed: {error:?}")]
100 DNSResolutionFailed {
101 /// The underlying DNS resolution error
102 error: hickory_resolver::ResolveError,
103 },
104
105 /// Occurs when DNS TXT record lookup fails (generic version for when hickory-dns is not enabled)
106 #[cfg(not(feature = "hickory-dns"))]
107 #[error("error-atproto-identity-resolve-4 DNS resolution failed")]
108 DNSResolutionFailed,
109
110 /// Occurs when HTTP request to .well-known/atproto-did endpoint fails
111 #[error("error-atproto-identity-resolve-5 HTTP resolution failed: {error:?}")]
112 HTTPResolutionFailed {
113 /// The underlying HTTP error
114 error: reqwest::Error,
115 },
116
117 /// Occurs when HTTP response from .well-known/atproto-did doesn't start with "did:"
118 #[error(
119 "error-atproto-identity-resolve-6 Invalid HTTP resolution response: expected DID format"
120 )]
121 InvalidHTTPResolutionResponse,
122
123 /// Occurs when input cannot be parsed as a valid handle or DID
124 #[error("error-atproto-identity-resolve-7 Invalid input format: expected valid handle or DID")]
125 InvalidInput,
126
127 /// Occurs when subject resolution results in a handle instead of expected DID
128 #[error("error-atproto-identity-resolve-8 Subject resolved to handle instead of DID")]
129 SubjectResolvedToHandle,
130}
131
132/// Error types that can occur when working with PLC DIDs
133#[derive(Debug, Error)]
134pub enum PLCDIDError {
135 /// Occurs when the HTTP request to the PLC directory fails
136 #[error("error-atproto-identity-plc-1 HTTP request failed: {url} {error}")]
137 HttpRequestFailed {
138 /// The URL that was requested
139 url: String,
140 /// The underlying HTTP error
141 error: reqwest::Error,
142 },
143
144 /// Occurs when the DID document cannot be parsed from the PLC directory response
145 #[error("error-atproto-identity-plc-2 Failed to parse DID document: {url} {error}")]
146 DocumentParseFailed {
147 /// The URL that was requested
148 url: String,
149 /// The underlying parse error
150 error: reqwest::Error,
151 },
152}
153
154/// Error types that can occur when working with cryptographic keys
155#[derive(Debug, Error)]
156pub enum KeyError {
157 /// Occurs when multibase decoding of a key fails
158 #[error("error-atproto-identity-key-1 Error decoding key: {error:?}")]
159 DecodeError {
160 /// The underlying multibase decode error
161 error: multibase::Error,
162 },
163
164 /// Occurs when ECDSA signature parsing fails
165 #[error("error-atproto-identity-key-2 Signature parsing failed: {error:?}")]
166 SignatureError {
167 /// The underlying signature parsing error
168 error: ecdsa::signature::Error,
169 },
170
171 /// Occurs when P-256 key operations fail
172 #[error("error-atproto-identity-key-3 P-256 key operation failed: {error:?}")]
173 P256Error {
174 /// The underlying P-256 key error
175 error: p256::ecdsa::Error,
176 },
177
178 /// Occurs when P-384 key operations fail
179 #[error("error-atproto-identity-key-4 P-384 key operation failed: {error:?}")]
180 P384Error {
181 /// The underlying P-384 key error
182 error: p384::ecdsa::Error,
183 },
184
185 /// Occurs when K-256 key operations fail
186 #[error("error-atproto-identity-key-5 K-256 key operation failed: {error:?}")]
187 K256Error {
188 /// The underlying K-256 key error
189 error: k256::ecdsa::Error,
190 },
191
192 /// Occurs when ECDSA cryptographic operations fail
193 #[error("error-atproto-identity-key-6 ECDSA operation failed: {error:?}")]
194 ECDSAError {
195 /// The underlying ECDSA error
196 error: ecdsa::Error,
197 },
198
199 /// Occurs when secret key parsing or operations fail
200 #[error("error-atproto-identity-key-7 Secret key operation failed: {error:?}")]
201 SecretKeyError {
202 /// The underlying secret key error
203 error: ecdsa::elliptic_curve::Error,
204 },
205
206 /// Occurs when attempting to sign content with a public key instead of a private key
207 #[error("error-atproto-identity-key-8 Private key required for signature")]
208 PrivateKeyRequiredForSignature,
209
210 /// Occurs when attempting to generate a public key directly
211 #[error(
212 "error-atproto-identity-key-9 Public key generation not supported: generate private key instead"
213 )]
214 PublicKeyGenerationNotSupported,
215
216 /// Occurs when the decoded key data is too short to identify the key type
217 #[error("error-atproto-identity-key-10 Unidentified key type: key data too short")]
218 UnidentifiedKeyType,
219
220 /// Occurs when the multibase key type prefix is not recognized
221 #[error("error-atproto-identity-key-11 Invalid multibase key type: {prefix:?}")]
222 InvalidMultibaseKeyType {
223 /// The unrecognized key type prefix
224 prefix: Vec<u8>,
225 },
226
227 /// Occurs when JWK format conversion fails for supported key types
228 #[error("error-atproto-identity-key-12 JWK format conversion failed: {error}")]
229 JWKConversionFailed {
230 /// The underlying conversion error
231 error: String,
232 },
233}
234
235/// Error types that can occur when working with storage operations
236#[derive(Debug, Error)]
237pub enum StorageError {
238 /// Occurs when cache lock acquisition fails during document retrieval operations
239 #[error(
240 "error-atproto-identity-storage-1 Cache lock acquisition failed for get operation: {details}"
241 )]
242 CacheLockFailedGet {
243 /// Details about the lock failure
244 details: String,
245 },
246
247 /// Occurs when cache lock acquisition fails during document storage operations
248 #[error(
249 "error-atproto-identity-storage-2 Cache lock acquisition failed for store operation: {details}"
250 )]
251 CacheLockFailedStore {
252 /// Details about the lock failure
253 details: String,
254 },
255
256 /// Occurs when cache lock acquisition fails during document deletion operations
257 #[error(
258 "error-atproto-identity-storage-3 Cache lock acquisition failed for delete operation: {details}"
259 )]
260 CacheLockFailedDelete {
261 /// Details about the lock failure
262 details: String,
263 },
264}