Skip to main content

ic_query/subnet_catalog/
error.rs

1use crate::subnet_catalog::RoutingRange;
2use thiserror::Error as ThisError;
3
4///
5/// CatalogError
6///
7/// Errors returned while parsing, validating, or resolving a subnet catalog.
8///
9
10#[derive(Debug, ThisError)]
11pub enum CatalogError {
12    #[error(transparent)]
13    Json(#[from] serde_json::Error),
14
15    #[error("unsupported subnet catalog schema version {found}; supported version is {supported}")]
16    UnsupportedSchemaVersion { found: u32, supported: u32 },
17
18    /// Catalog network identity does not match the required authority.
19    #[error("subnet catalog network is {actual:?}; expected {expected:?}")]
20    NetworkMismatch {
21        /// Required network identity.
22        expected: String,
23        /// Catalog network identity.
24        actual: String,
25    },
26
27    /// Catalog Registry canister identity does not match mainnet authority.
28    #[error("subnet catalog Registry canister is {actual}; expected {expected}")]
29    RegistryCanisterMismatch {
30        /// Required Registry canister principal.
31        expected: String,
32        /// Catalog Registry canister principal.
33        actual: String,
34    },
35
36    /// Registry version zero cannot identify an authority snapshot.
37    #[error("subnet catalog Registry version must be greater than zero")]
38    InvalidRegistryVersion,
39
40    #[error("subnet catalog must contain at least one subnet")]
41    EmptySubnets,
42
43    #[error("subnet catalog must contain at least one routing range")]
44    EmptyRoutingRanges,
45
46    #[error("invalid principal in {field}: {value}: {reason}")]
47    InvalidPrincipal {
48        field: &'static str,
49        value: String,
50        reason: String,
51    },
52
53    #[error("duplicate subnet principal in catalog: {subnet_principal}")]
54    DuplicateSubnet { subnet_principal: String },
55
56    /// Subnet rows are not strictly ordered by canonical principal text.
57    #[error("noncanonical subnet order: {previous} must sort before {current}")]
58    NonCanonicalSubnetOrder {
59        /// Previous Subnet principal.
60        previous: String,
61        /// Current Subnet principal.
62        current: String,
63    },
64
65    #[error("routing range references unknown subnet: {subnet_principal}")]
66    UnknownRoutingSubnet { subnet_principal: String },
67
68    #[error(
69        "invalid routing range for {subnet_principal}: start {start_canister_id} sorts after end {end_canister_id}"
70    )]
71    InvalidRoutingRange {
72        subnet_principal: String,
73        start_canister_id: String,
74        end_canister_id: String,
75    },
76
77    #[error("overlapping routing ranges: {first} overlaps {second}")]
78    OverlappingRoutingRanges {
79        first: Box<RoutingRange>,
80        second: Box<RoutingRange>,
81    },
82
83    /// Routing ranges are not strictly ordered by principal bytes and target.
84    #[error("noncanonical routing range order: {previous} must sort before {current}")]
85    NonCanonicalRoutingOrder {
86        /// Previous routing range.
87        previous: Box<RoutingRange>,
88        /// Current routing range.
89        current: Box<RoutingRange>,
90    },
91
92    /// Derived Subnet kind contradicts its raw Registry numeric code.
93    #[error(
94        "subnet {subnet_principal} kind {actual} contradicts Registry subnet_type={registry_subnet_type}; expected {expected}"
95    )]
96    SubnetKindMismatch {
97        /// Subnet principal.
98        subnet_principal: String,
99        /// Raw Registry numeric discriminant.
100        registry_subnet_type: i32,
101        /// Kind required by the raw discriminant.
102        expected: String,
103        /// Kind supplied by the raw catalog.
104        actual: String,
105    },
106
107    /// Charging default contradicts the raw Registry Subnet kind.
108    #[error(
109        "subnet {subnet_principal} charges_apply_by_default={actual} contradicts expected {expected}"
110    )]
111    ChargingPolicyMismatch {
112        /// Subnet principal.
113        subnet_principal: String,
114        /// Charging default required by the raw kind.
115        expected: bool,
116        /// Charging default supplied by the raw catalog.
117        actual: bool,
118    },
119
120    /// Classification metadata contradicts its policy or source.
121    #[error("subnet {subnet_principal} classification field {field} is invalid: {reason}")]
122    ClassificationMismatch {
123        /// Subnet principal.
124        subnet_principal: String,
125        /// Invalid classification field.
126        field: &'static str,
127        /// Deterministic mismatch reason.
128        reason: String,
129    },
130
131    /// A provenance timestamp is not valid canonical UTC text.
132    #[error("invalid catalog timestamp in {field}: {value:?}")]
133    InvalidTimestamp {
134        /// Timestamp field.
135        field: &'static str,
136        /// Invalid timestamp text.
137        value: String,
138    },
139
140    /// A provenance timestamp is beyond the caller's future-skew policy.
141    #[error(
142        "catalog timestamp in {field} is in the future: {value}; latest accepted unix time is {latest_allowed_unix_secs}"
143    )]
144    FutureTimestamp {
145        /// Timestamp field.
146        field: &'static str,
147        /// Future timestamp text.
148        value: String,
149        /// Latest accepted Unix time.
150        latest_allowed_unix_secs: u64,
151    },
152
153    /// A source endpoint is not a clean credential-free HTTP(S) base URL.
154    #[error("invalid catalog source endpoint {endpoint:?}: {reason}")]
155    InvalidSourceEndpoint {
156        /// Invalid endpoint.
157        endpoint: String,
158        /// Endpoint validation reason.
159        reason: String,
160    },
161
162    /// Provenance evidence is internally inconsistent.
163    #[error("invalid catalog provenance field {field}: {reason}")]
164    InvalidProvenance {
165        /// Invalid provenance field.
166        field: &'static str,
167        /// Deterministic validation reason.
168        reason: String,
169    },
170
171    /// The build does not yet have a verifier for the claimed assurance level.
172    #[error(
173        "unsupported catalog assurance {assurance}; this build can validate uncertified_query only"
174    )]
175    UnsupportedAssurance {
176        /// Unsupported assurance label.
177        assurance: String,
178    },
179
180    /// Classification policy schema is not supported by this build.
181    #[error("classification policy schema {found} is unsupported; expected {supported}")]
182    ClassificationPolicyVersionMismatch {
183        /// Catalog policy schema.
184        found: u32,
185        /// Supported policy schema.
186        supported: u32,
187    },
188
189    /// Classification policy digest does not match this build.
190    #[error("classification policy digest mismatch: found {actual}; expected {expected}")]
191    ClassificationPolicyDigestMismatch {
192        /// Expected policy digest.
193        expected: String,
194        /// Catalog policy digest.
195        actual: String,
196    },
197
198    /// Resolver implementation identity does not match this build.
199    #[error(
200        "resolver policy mismatch: version {actual_version} backend {actual_backend:?}; expected version {expected_version} backend {expected_backend:?}"
201    )]
202    ResolverPolicyMismatch {
203        /// Expected resolver schema.
204        expected_version: u32,
205        /// Catalog resolver schema.
206        actual_version: u32,
207        /// Expected resolver backend.
208        expected_backend: String,
209        /// Catalog resolver backend.
210        actual_backend: String,
211    },
212
213    /// Catalog digest is not exactly 32 lowercase hexadecimal bytes.
214    #[error("invalid catalog digest {value:?}; expected 64 lowercase hexadecimal characters")]
215    InvalidCatalogDigest {
216        /// Invalid digest text.
217        value: String,
218    },
219
220    /// Stored catalog digest does not match the canonical authority payload.
221    #[error("catalog digest mismatch: found {actual}; expected {expected}")]
222    CatalogDigestMismatch {
223        /// Recomputed digest.
224        expected: String,
225        /// Stored digest.
226        actual: String,
227    },
228
229    #[error("subnet principal {subnet_principal} was not found in the cached catalog")]
230    UnknownSubnet { subnet_principal: String },
231
232    #[error("principal prefix {prefix:?} did not match cached subnet principals")]
233    PrincipalPrefixNotFound { prefix: String },
234
235    #[error("principal prefix {prefix:?} is ambiguous; matches: {matches:?}")]
236    AmbiguousPrincipalPrefix {
237        prefix: String,
238        matches: Vec<String>,
239    },
240
241    #[error(
242        "canister principal {canister_principal} was not covered by cached routing ranges at registry_version={registry_version}, catalog_schema_version={catalog_schema_version}"
243    )]
244    RouteNotFound {
245        canister_principal: String,
246        registry_version: u64,
247        catalog_schema_version: u32,
248    },
249}