Skip to main content

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 serde::{Deserialize, Serialize};
21use thiserror::Error;
22
23/// Error types that can occur when working with Web DIDs
24#[derive(Debug, Error)]
25pub enum WebDIDError {
26    /// Occurs when the DID is missing the 'did:web:' prefix
27    #[error("error-atproto-identity-web-1 Invalid DID format: missing 'did:web:' prefix")]
28    InvalidDIDPrefix,
29
30    /// Occurs when the DID is missing a hostname component
31    #[error("error-atproto-identity-web-2 Invalid DID format: missing hostname component")]
32    MissingHostname,
33
34    /// Occurs when the HTTP request to fetch the DID document fails
35    #[error("error-atproto-identity-web-3 HTTP request failed: {url} {error}")]
36    HttpRequestFailed {
37        /// The URL that was requested
38        url: String,
39        /// The underlying HTTP error
40        error: reqwest::Error,
41    },
42
43    /// Occurs when the DID document cannot be parsed from the HTTP response
44    #[error("error-atproto-identity-web-4 Failed to parse DID document: {url} {error}")]
45    DocumentParseFailed {
46        /// The URL that was requested
47        url: String,
48        /// The underlying parse error
49        error: reqwest::Error,
50    },
51}
52
53/// Error types that can occur when working with configuration
54#[derive(Debug, Error)]
55pub enum ConfigError {
56    /// Occurs when a required environment variable is not set
57    #[error("error-atproto-identity-config-1 Required environment variable not found: {name}")]
58    MissingEnvironmentVariable {
59        /// The name of the missing environment variable
60        name: String,
61    },
62
63    /// Occurs when parsing an IP address from nameserver configuration fails
64    #[error("error-atproto-identity-config-2 Unable to parse nameserver IP: {value}")]
65    InvalidNameserverIP {
66        /// The invalid IP address value that could not be parsed
67        value: String,
68    },
69
70    /// Occurs when version information cannot be determined
71    #[error(
72        "error-atproto-identity-config-3 Version information not available: GIT_HASH or CARGO_PKG_VERSION must be set"
73    )]
74    VersionNotAvailable,
75}
76
77/// Error types that can occur when resolving AT Protocol identities
78#[derive(Debug, Error)]
79pub enum ResolveError {
80    /// Occurs when multiple different DIDs are found via DNS TXT record lookup
81    #[error(
82        "error-atproto-identity-resolve-1 Multiple DIDs resolved for handle: expected single DID"
83    )]
84    MultipleDIDsFound,
85
86    /// Occurs when no DIDs are found via either DNS or HTTP resolution methods
87    #[error(
88        "error-atproto-identity-resolve-2 No DIDs resolved for handle: no resolution methods succeeded"
89    )]
90    NoDIDsFound,
91
92    /// Occurs when DNS and HTTP resolution return different DIDs for the same handle
93    #[error(
94        "error-atproto-identity-resolve-3 Conflicting DIDs found for handle: DNS and HTTP resolution returned different results"
95    )]
96    ConflictingDIDsFound,
97
98    /// Occurs when DNS TXT record lookup fails
99    #[cfg(feature = "hickory-dns")]
100    #[error("error-atproto-identity-resolve-4 DNS resolution failed: {error:?}")]
101    DNSResolutionFailed {
102        /// The underlying DNS resolution error
103        error: hickory_resolver::ResolveError,
104    },
105
106    /// Occurs when DNS TXT record lookup fails (generic version for when hickory-dns is not enabled)
107    #[cfg(not(feature = "hickory-dns"))]
108    #[error("error-atproto-identity-resolve-4 DNS resolution failed")]
109    DNSResolutionFailed,
110
111    /// Occurs when HTTP request to .well-known/atproto-did endpoint fails
112    #[error("error-atproto-identity-resolve-5 HTTP resolution failed: {error:?}")]
113    HTTPResolutionFailed {
114        /// The underlying HTTP error
115        error: reqwest::Error,
116    },
117
118    /// Occurs when HTTP response from .well-known/atproto-did doesn't start with "did:"
119    #[error(
120        "error-atproto-identity-resolve-6 Invalid HTTP resolution response: expected DID format"
121    )]
122    InvalidHTTPResolutionResponse,
123
124    /// Occurs when input cannot be parsed as a valid handle or DID
125    #[error("error-atproto-identity-resolve-7 Invalid input format: expected valid handle or DID")]
126    InvalidInput,
127
128    /// Occurs when subject resolution results in a handle instead of expected DID
129    #[error("error-atproto-identity-resolve-8 Subject resolved to handle instead of DID")]
130    SubjectResolvedToHandle,
131}
132
133/// Error types that can occur when working with PLC DIDs
134#[derive(Debug, Error)]
135pub enum PLCDIDError {
136    /// Occurs when the HTTP request to the PLC directory fails
137    #[error("error-atproto-identity-plc-1 HTTP request failed: {url} {error}")]
138    HttpRequestFailed {
139        /// The URL that was requested
140        url: String,
141        /// The underlying HTTP error
142        error: reqwest::Error,
143    },
144
145    /// Occurs when the DID document cannot be parsed from the PLC directory response
146    #[error("error-atproto-identity-plc-2 Failed to parse DID document: {url} {error}")]
147    DocumentParseFailed {
148        /// The URL that was requested
149        url: String,
150        /// The underlying parse error
151        error: reqwest::Error,
152    },
153
154    /// Occurs when a DID string cannot be parsed as a valid did:plc identifier
155    #[error("error-atproto-identity-plc-3 Invalid DID format: {details}")]
156    InvalidDidFormat {
157        /// Details about the format violation
158        details: String,
159    },
160
161    /// Occurs when base32 decoding fails
162    #[error("error-atproto-identity-plc-4 Invalid base32 encoding: {details}")]
163    InvalidBase32 {
164        /// Details about the decoding failure
165        details: String,
166    },
167
168    /// Occurs when base64url decoding fails
169    #[error("error-atproto-identity-plc-5 Invalid base64url encoding: {details}")]
170    InvalidBase64Url {
171        /// Details about the decoding failure
172        details: String,
173    },
174
175    /// Occurs when an operation exceeds the maximum allowed size
176    #[error("error-atproto-identity-plc-6 Operation exceeds size limit: {size} bytes (max {max})")]
177    OperationTooLarge {
178        /// Actual size of the operation in bytes
179        size: usize,
180        /// Maximum allowed size in bytes
181        max: usize,
182    },
183
184    /// Occurs when rotation keys fail validation
185    #[error("error-atproto-identity-plc-7 Invalid rotation keys: {details}")]
186    InvalidRotationKeys {
187        /// Details about the validation failure
188        details: String,
189    },
190
191    /// Occurs when verification methods fail validation
192    #[error("error-atproto-identity-plc-8 Invalid verification methods: {details}")]
193    InvalidVerificationMethods {
194        /// Details about the validation failure
195        details: String,
196    },
197
198    /// Occurs when a service endpoint fails validation
199    #[error("error-atproto-identity-plc-9 Invalid service endpoint: {details}")]
200    InvalidService {
201        /// Details about the validation failure
202        details: String,
203    },
204
205    /// Occurs when a field exceeds its maximum entry count
206    #[error("error-atproto-identity-plc-10 Too many entries in {field}: max {max}, got {actual}")]
207    TooManyEntries {
208        /// The field that has too many entries
209        field: String,
210        /// Maximum allowed count
211        max: usize,
212        /// Actual count
213        actual: usize,
214    },
215
216    /// Occurs when a duplicate value is found in a field
217    #[error("error-atproto-identity-plc-11 Duplicate entry in {field}: {value}")]
218    DuplicateEntry {
219        /// The field containing the duplicate
220        field: String,
221        /// The duplicated value
222        value: String,
223    },
224
225    /// Occurs when DAG-CBOR encoding fails
226    #[error("error-atproto-identity-plc-12 DAG-CBOR encoding failed: {details}")]
227    DagCborEncodeFailed {
228        /// Details about the encoding failure
229        details: String,
230    },
231
232    /// Occurs when DAG-CBOR decoding fails
233    #[error("error-atproto-identity-plc-13 DAG-CBOR decoding failed: {details}")]
234    DagCborDecodeFailed {
235        /// Details about the decoding failure
236        details: String,
237    },
238
239    /// Occurs when an operation's signature cannot be verified
240    #[error("error-atproto-identity-plc-14 Signature verification failed")]
241    SignatureVerificationFailed,
242
243    /// Occurs when a CID is invalid or cannot be computed
244    #[error("error-atproto-identity-plc-15 Invalid CID: {details}")]
245    InvalidCid {
246        /// Details about the CID error
247        details: String,
248    },
249
250    /// Occurs when an operation has an unrecognized type
251    #[error("error-atproto-identity-plc-16 Invalid operation type: {details}")]
252    InvalidOperationType {
253        /// Details about the invalid operation type
254        details: String,
255    },
256
257    /// Occurs when a required field is missing from an operation
258    #[error("error-atproto-identity-plc-17 Missing required field: {field}")]
259    MissingField {
260        /// The name of the missing field
261        field: String,
262    },
263
264    /// Occurs when operation chain validation fails
265    #[error("error-atproto-identity-plc-18 Chain validation failed: {details}")]
266    ChainValidationFailed {
267        /// Details about the validation failure
268        details: String,
269    },
270
271    /// Occurs when an operation chain is empty
272    #[error("error-atproto-identity-plc-19 Empty operation chain")]
273    EmptyChain,
274
275    /// Occurs when the first operation in a chain is not a genesis operation
276    #[error("error-atproto-identity-plc-20 First operation must be genesis")]
277    FirstOperationNotGenesis,
278
279    /// Occurs when an operation references an invalid previous operation
280    #[error("error-atproto-identity-plc-21 Invalid prev reference: {details}")]
281    InvalidPrev {
282        /// Details about the invalid reference
283        details: String,
284    },
285
286    /// Occurs when fork resolution fails
287    #[error("error-atproto-identity-plc-22 Fork resolution error: {details}")]
288    ForkResolutionError {
289        /// Details about the fork resolution failure
290        details: String,
291    },
292
293    /// Occurs when an also-known-as URI is invalid
294    #[error("error-atproto-identity-plc-23 Invalid also-known-as URI: {details}")]
295    InvalidAlsoKnownAs {
296        /// Details about the invalid URI
297        details: String,
298    },
299
300    /// Occurs when a timestamp is invalid
301    #[error("error-atproto-identity-plc-24 Invalid timestamp: {details}")]
302    InvalidTimestamp {
303        /// Details about the invalid timestamp
304        details: String,
305    },
306
307    /// Occurs when submitting an operation to the PLC directory fails
308    #[error("error-atproto-identity-plc-25 Submission failed: {url} status {status}: {body}")]
309    SubmissionFailed {
310        /// The URL that was requested
311        url: String,
312        /// The HTTP status code
313        status: u16,
314        /// The response body
315        body: String,
316    },
317
318    /// Occurs when fetching the audit log from the PLC directory fails
319    #[error("error-atproto-identity-plc-26 Audit log fetch failed: {url} {error}")]
320    AuditLogFetchFailed {
321        /// The URL that was requested
322        url: String,
323        /// The underlying HTTP error
324        error: reqwest::Error,
325    },
326
327    /// Occurs when the audit log response cannot be parsed
328    #[error("error-atproto-identity-plc-27 Audit log parse failed: {url} {error}")]
329    AuditLogParseFailed {
330        /// The URL that was requested
331        url: String,
332        /// The underlying parse error
333        error: reqwest::Error,
334    },
335}
336
337/// Error types that can occur when working with cryptographic keys
338#[derive(Debug, Error)]
339pub enum KeyError {
340    /// Occurs when multibase decoding of a key fails
341    #[error("error-atproto-identity-key-1 Error decoding key: {error:?}")]
342    DecodeError {
343        /// The underlying multibase decode error
344        error: multibase::Error,
345    },
346
347    /// Occurs when ECDSA signature parsing fails
348    #[error("error-atproto-identity-key-2 Signature parsing failed: {error:?}")]
349    SignatureError {
350        /// The underlying signature parsing error
351        error: ecdsa::signature::Error,
352    },
353
354    /// Occurs when P-256 key operations fail
355    #[error("error-atproto-identity-key-3 P-256 key operation failed: {error:?}")]
356    P256Error {
357        /// The underlying P-256 key error
358        error: p256::ecdsa::Error,
359    },
360
361    /// Occurs when P-384 key operations fail
362    #[error("error-atproto-identity-key-4 P-384 key operation failed: {error:?}")]
363    P384Error {
364        /// The underlying P-384 key error
365        error: p384::ecdsa::Error,
366    },
367
368    /// Occurs when K-256 key operations fail
369    #[error("error-atproto-identity-key-5 K-256 key operation failed: {error:?}")]
370    K256Error {
371        /// The underlying K-256 key error
372        error: k256::ecdsa::Error,
373    },
374
375    /// Occurs when ECDSA cryptographic operations fail
376    #[error("error-atproto-identity-key-6 ECDSA operation failed: {error:?}")]
377    ECDSAError {
378        /// The underlying ECDSA error
379        error: ecdsa::Error,
380    },
381
382    /// Occurs when secret key parsing or operations fail
383    #[error("error-atproto-identity-key-7 Secret key operation failed: {error:?}")]
384    SecretKeyError {
385        /// The underlying secret key error
386        error: ecdsa::elliptic_curve::Error,
387    },
388
389    /// Occurs when attempting to sign content with a public key instead of a private key
390    #[error("error-atproto-identity-key-8 Private key required for signature")]
391    PrivateKeyRequiredForSignature,
392
393    /// Occurs when attempting to generate a public key directly
394    #[error(
395        "error-atproto-identity-key-9 Public key generation not supported: generate private key instead"
396    )]
397    PublicKeyGenerationNotSupported,
398
399    /// Occurs when the decoded key data is too short to identify the key type
400    #[error("error-atproto-identity-key-10 Unidentified key type: key data too short")]
401    UnidentifiedKeyType,
402
403    /// Occurs when the multibase key type prefix is not recognized
404    #[error("error-atproto-identity-key-11 Invalid multibase key type: {prefix:?}")]
405    InvalidMultibaseKeyType {
406        /// The unrecognized key type prefix
407        prefix: Vec<u8>,
408    },
409
410    /// Occurs when JWK format conversion fails for supported key types
411    #[error("error-atproto-identity-key-12 JWK format conversion failed: {error}")]
412    JWKConversionFailed {
413        /// The underlying conversion error
414        error: String,
415    },
416
417    /// Occurs when Ed25519 key operations fail
418    #[error("error-atproto-identity-key-13 Ed25519 key operation failed: {error}")]
419    Ed25519Error {
420        /// Description of the Ed25519 error
421        error: String,
422    },
423}
424
425/// Error types that can occur when working with WebVH DIDs
426#[derive(Debug, Error)]
427pub enum WebVHDIDError {
428    /// Occurs when the DID is missing the 'did:webvh:' prefix
429    #[error("error-atproto-identity-webvh-1 Invalid DID format: missing 'did:webvh:' prefix")]
430    InvalidDIDPrefix,
431
432    /// Occurs when the DID is missing a SCID component
433    #[error("error-atproto-identity-webvh-2 Invalid DID format: missing SCID component")]
434    MissingSCID,
435
436    /// Occurs when the DID is missing a hostname component
437    #[error("error-atproto-identity-webvh-3 Invalid DID format: missing hostname component")]
438    MissingHostname,
439
440    /// Occurs when the HTTP request to fetch the DID log fails
441    #[error("error-atproto-identity-webvh-4 HTTP request failed: {url} {error}")]
442    HttpRequestFailed {
443        /// The URL that was requested
444        url: String,
445        /// The underlying HTTP error
446        error: reqwest::Error,
447    },
448
449    /// Occurs when a log entry cannot be parsed from the JSONL response
450    #[error("error-atproto-identity-webvh-5 Failed to parse log entry at line {line}: {details}")]
451    LogEntryParseFailed {
452        /// The line number (1-indexed) that failed to parse
453        line: usize,
454        /// Details about the parse failure
455        details: String,
456    },
457
458    /// Occurs when the DID log file is empty
459    #[error("error-atproto-identity-webvh-6 Empty log file")]
460    EmptyLog,
461
462    /// Occurs when a version ID is invalid or has incorrect format
463    #[error("error-atproto-identity-webvh-7 Invalid version ID at entry {entry}: {details}")]
464    InvalidVersionId {
465        /// The entry number (1-indexed) with the invalid version ID
466        entry: usize,
467        /// Details about the validation failure
468        details: String,
469    },
470
471    /// Occurs when version times are not monotonically increasing
472    #[error(
473        "error-atproto-identity-webvh-8 Version time not monotonically increasing at entry {entry}"
474    )]
475    VersionTimeNotMonotonic {
476        /// The entry number (1-indexed) with the non-monotonic timestamp
477        entry: usize,
478    },
479
480    /// Occurs when the SCID does not match the computed value from the genesis entry
481    #[error("error-atproto-identity-webvh-9 SCID verification failed on genesis entry")]
482    SCIDVerificationFailed,
483
484    /// Occurs when an entry's hash does not match the expected value in the version ID
485    #[error("error-atproto-identity-webvh-10 Entry hash verification failed at entry {entry}")]
486    EntryHashVerificationFailed {
487        /// The entry number (1-indexed) with the hash mismatch
488        entry: usize,
489    },
490
491    /// Occurs when a Data Integrity proof cannot be verified
492    #[error(
493        "error-atproto-identity-webvh-11 Proof verification failed at entry {entry}: {details}"
494    )]
495    ProofVerificationFailed {
496        /// The entry number (1-indexed) with the failed proof
497        entry: usize,
498        /// Details about the verification failure
499        details: String,
500    },
501
502    /// Occurs when pre-rotation key hashes do not match
503    #[error("error-atproto-identity-webvh-12 Pre-rotation key hash mismatch at entry {entry}")]
504    PreRotationKeyMismatch {
505        /// The entry number (1-indexed) with the key mismatch
506        entry: usize,
507    },
508
509    /// Occurs when log entry parameters are invalid
510    #[error("error-atproto-identity-webvh-13 Invalid parameters at entry {entry}: {details}")]
511    InvalidParameters {
512        /// The entry number (1-indexed) with the invalid parameters
513        entry: usize,
514        /// Details about the validation failure
515        details: String,
516    },
517
518    /// Occurs when witness verification fails
519    #[error(
520        "error-atproto-identity-webvh-14 Witness verification failed at entry {entry}: {details}"
521    )]
522    WitnessVerificationFailed {
523        /// The entry number (1-indexed) with the failed witness verification
524        entry: usize,
525        /// Details about the verification failure
526        details: String,
527    },
528
529    /// Occurs when the DID document cannot be extracted from the log state
530    #[error("error-atproto-identity-webvh-15 DID document extraction failed: {details}")]
531    DocumentExtractionFailed {
532        /// Details about the extraction failure
533        details: String,
534    },
535
536    /// Occurs when the SCID format is invalid
537    #[error("error-atproto-identity-webvh-16 Invalid SCID format: {details}")]
538    InvalidSCIDFormat {
539        /// Details about the format violation
540        details: String,
541    },
542
543    /// Occurs when the resolved DID has been deactivated
544    #[error("error-atproto-identity-webvh-17 Deactivated DID: {did}")]
545    DeactivatedDID {
546        /// The deactivated DID
547        did: String,
548    },
549
550    /// Occurs when a requested version is not found in the log
551    #[error("error-atproto-identity-webvh-18 Version not found: {details}")]
552    VersionNotFound {
553        /// Details about the lookup that failed
554        details: String,
555    },
556
557    /// Occurs when a version time is in the future
558    #[error(
559        "error-atproto-identity-webvh-19 Version time is in the future at entry {entry}: {version_time}"
560    )]
561    VersionTimeInFuture {
562        /// The entry number (1-indexed) with the future timestamp
563        entry: usize,
564        /// The future timestamp value
565        version_time: String,
566    },
567
568    /// Occurs when the DIDDoc id does not match the DID
569    #[error(
570        "error-atproto-identity-webvh-20 DIDDoc id mismatch at entry {entry}: expected '{expected}', found '{found}'"
571    )]
572    DIDDocIdMismatch {
573        /// The entry number (1-indexed) with the mismatch
574        entry: usize,
575        /// The expected DID
576        expected: String,
577        /// The actual id found in the DIDDoc
578        found: String,
579    },
580
581    /// Occurs when the hash algorithm in the SCID is not supported
582    #[error("error-atproto-identity-webvh-21 Unsupported hash algorithm in SCID: {details}")]
583    UnsupportedHashAlgorithm {
584        /// Details about the unsupported algorithm
585        details: String,
586    },
587
588    /// Occurs when an unknown parameter is found in a log entry
589    #[error("error-atproto-identity-webvh-22 Unknown parameter at entry {entry}: {parameter}")]
590    UnknownParameter {
591        /// The entry number (1-indexed) with the unknown parameter
592        entry: usize,
593        /// The unknown parameter name
594        parameter: String,
595    },
596
597    /// Occurs when hostname normalization fails
598    #[error("error-atproto-identity-webvh-23 Hostname normalization failed: {details}")]
599    HostnameNormalizationFailed {
600        /// Details about the normalization failure
601        details: String,
602    },
603
604    /// Occurs when genesis document creation fails
605    #[error("error-atproto-identity-webvh-24 Genesis creation failed: {details}")]
606    GenesisCreationFailed {
607        /// Details about the creation failure
608        details: String,
609    },
610
611    /// Occurs when update entry creation fails
612    #[error("error-atproto-identity-webvh-25 Update creation failed: {details}")]
613    UpdateCreationFailed {
614        /// Details about the creation failure
615        details: String,
616    },
617
618    /// Occurs when signing a log entry fails
619    #[error("error-atproto-identity-webvh-26 Signing failed: {details}")]
620    SigningFailed {
621        /// Details about the signing failure
622        details: String,
623    },
624}
625
626/// Resolution error codes as defined by the did:webvh specification.
627#[derive(Debug, Clone, Serialize, Deserialize)]
628#[serde(rename_all = "camelCase")]
629pub struct ResolutionError {
630    /// Error code: "notFound" or "invalidDid".
631    pub error: ResolutionErrorCode,
632    /// Optional RFC 9457 problem details.
633    #[serde(skip_serializing_if = "Option::is_none")]
634    pub problem_details: Option<ProblemDetails>,
635}
636
637/// Resolution error code per the did:webvh specification.
638#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
639pub enum ResolutionErrorCode {
640    /// The DID was not found.
641    #[serde(rename = "notFound")]
642    NotFound,
643    /// The DID is invalid or failed verification.
644    #[serde(rename = "invalidDid")]
645    InvalidDid,
646}
647
648/// RFC 9457 Problem Details structure.
649#[derive(Debug, Clone, Serialize, Deserialize)]
650pub struct ProblemDetails {
651    /// Problem type URI.
652    #[serde(skip_serializing_if = "Option::is_none")]
653    pub r#type: Option<String>,
654    /// Short human-readable summary.
655    pub title: String,
656    /// Detailed explanation.
657    #[serde(skip_serializing_if = "Option::is_none")]
658    pub detail: Option<String>,
659}
660
661impl WebVHDIDError {
662    /// Converts this error to a spec-compliant resolution error.
663    pub fn to_resolution_error(&self) -> ResolutionError {
664        match self {
665            WebVHDIDError::HttpRequestFailed { .. } | WebVHDIDError::EmptyLog => ResolutionError {
666                error: ResolutionErrorCode::NotFound,
667                problem_details: Some(ProblemDetails {
668                    r#type: None,
669                    title: "DID not found".to_string(),
670                    detail: Some(self.to_string()),
671                }),
672            },
673            WebVHDIDError::InvalidDIDPrefix
674            | WebVHDIDError::MissingSCID
675            | WebVHDIDError::MissingHostname
676            | WebVHDIDError::InvalidSCIDFormat { .. } => ResolutionError {
677                error: ResolutionErrorCode::InvalidDid,
678                problem_details: Some(ProblemDetails {
679                    r#type: None,
680                    title: "Invalid DID format".to_string(),
681                    detail: Some(self.to_string()),
682                }),
683            },
684            _ => ResolutionError {
685                error: ResolutionErrorCode::InvalidDid,
686                problem_details: Some(ProblemDetails {
687                    r#type: None,
688                    title: "DID verification failed".to_string(),
689                    detail: Some(self.to_string()),
690                }),
691            },
692        }
693    }
694}
695
696/// Error types that can occur when working with storage operations
697#[derive(Debug, Error)]
698pub enum StorageError {
699    /// Occurs when cache lock acquisition fails during document retrieval operations
700    #[error(
701        "error-atproto-identity-storage-1 Cache lock acquisition failed for get operation: {details}"
702    )]
703    CacheLockFailedGet {
704        /// Details about the lock failure
705        details: String,
706    },
707
708    /// Occurs when cache lock acquisition fails during document storage operations
709    #[error(
710        "error-atproto-identity-storage-2 Cache lock acquisition failed for store operation: {details}"
711    )]
712    CacheLockFailedStore {
713        /// Details about the lock failure
714        details: String,
715    },
716
717    /// Occurs when cache lock acquisition fails during document deletion operations
718    #[error(
719        "error-atproto-identity-storage-3 Cache lock acquisition failed for delete operation: {details}"
720    )]
721    CacheLockFailedDelete {
722        /// Details about the lock failure
723        details: String,
724    },
725}