Skip to main content

dpp_domain/error/
dpp.rs

1//! Top-level error type for the DPP domain.
2
3use thiserror::Error;
4
5use crate::field_error::ValidationErrors;
6
7/// Top-level error type for the DPP domain.
8#[derive(Debug, Error)]
9#[non_exhaustive]
10pub enum DppError {
11    #[error("passport not found: {0}")]
12    NotFound(String),
13
14    #[error(
15        "passport is not in a state that allows this operation: current={current}, required={required}"
16    )]
17    InvalidTransition { current: String, required: String },
18
19    #[error("validation failed: {0}")]
20    Validation(ValidationErrors),
21
22    #[error("signing failed: {0}")]
23    Signing(String),
24
25    #[error("serialisation error: {0}")]
26    Serialisation(String),
27
28    /// A stored document carries an envelope key this build has removed, so it
29    /// predates a rename. Reading it would succeed while silently dropping the
30    /// field, which is why it is refused instead. See
31    /// [`crate::passport::REMOVED_ENVELOPE_KEYS`].
32    #[error(
33        "stored document carries removed envelope key `{removed}`, which was renamed to \
34         `{replacement}`: reading it would silently drop the field, so it is refused"
35    )]
36    RemovedEnvelopeKey {
37        /// The key as the stored document spells it.
38        removed: &'static str,
39        /// The key that replaced it.
40        replacement: &'static str,
41    },
42
43    /// A stored document's product group data predates the current schema by more
44    /// than the registered lens chain can bridge — e.g. a required field the
45    /// document was written before, with no source data anywhere to derive it
46    /// from. Not a bug to fix by writing a lens: some gaps have no honest
47    /// transform. See [`crate::schemas::lens`].
48    #[error("stored data does not match the current schema: {0}")]
49    SchemaIncompatible(#[from] crate::schemas::lens::UpcastError),
50
51    /// Returned when an attempt is made to delete or overwrite a passport that
52    /// has been published and is therefore subject to EU ESPR retention obligations.
53    /// Published passports must remain accessible for the legally defined period
54    /// under the applicable delegated act (typically 10–15 years).
55    #[error("passport is retention-locked: published passports cannot be deleted")]
56    RetentionLocked,
57
58    /// The forward walk from a passport to the record that replaced it could not
59    /// produce one answer.
60    ///
61    /// Distinct from `Ok(None)`, which is the ordinary "nothing supersedes this"
62    /// and is not an error. This is the store holding a shape the succession
63    /// edge does not permit — two records claiming one predecessor, a cycle, or
64    /// a chain longer than the walk will follow. Each is a reason the question
65    /// has **no** answer rather than a negative one, and returning the
66    /// first-sorted row instead would answer "which record am I holding" wrongly
67    /// and silently.
68    #[error("cannot resolve what supersedes passport {id}: {reason}")]
69    SuccessionUnresolvable {
70        /// The passport the walk started from.
71        id: String,
72        /// Which of the three shapes was met.
73        reason: String,
74    },
75
76    #[error("internal error: {0}")]
77    Internal(String),
78}
79
80impl From<ValidationErrors> for DppError {
81    fn from(errors: ValidationErrors) -> Self {
82        DppError::Validation(errors)
83    }
84}