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}