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 /// Catalog JSON could not be encoded or decoded.
13 #[error(transparent)]
14 Json(#[from] serde_json::Error),
15
16 /// The catalog uses a schema other than the current version-1 contract.
17 #[error("unsupported subnet catalog schema version {found}; supported version is {supported}")]
18 UnsupportedSchemaVersion {
19 /// Schema identifier found in the input.
20 found: u32,
21 /// Current supported schema identifier.
22 supported: u32,
23 },
24
25 /// Catalog network identity does not match the required authority.
26 #[error("subnet catalog network is {actual:?}; expected {expected:?}")]
27 NetworkMismatch {
28 /// Required network identity.
29 expected: String,
30 /// Catalog network identity.
31 actual: String,
32 },
33
34 /// Catalog Registry canister identity does not match mainnet authority.
35 #[error("subnet catalog Registry canister is {actual}; expected {expected}")]
36 RegistryCanisterMismatch {
37 /// Required Registry canister principal.
38 expected: String,
39 /// Catalog Registry canister principal.
40 actual: String,
41 },
42
43 /// Registry version zero cannot identify an authority snapshot.
44 #[error("subnet catalog Registry version must be greater than zero")]
45 InvalidRegistryVersion,
46
47 /// The catalog contains no Subnet records.
48 #[error("subnet catalog must contain at least one subnet")]
49 EmptySubnets,
50
51 /// The catalog contains no routing authority ranges.
52 #[error("subnet catalog must contain at least one routing range")]
53 EmptyRoutingRanges,
54
55 /// A catalog identity or routing boundary is not a valid IC principal.
56 #[error("invalid principal in {field}: {value}: {reason}")]
57 InvalidPrincipal {
58 /// Catalog field containing the invalid identity.
59 field: &'static str,
60 /// Rejected principal text.
61 value: String,
62 /// Principal parsing diagnostic.
63 reason: String,
64 },
65
66 /// Multiple Subnet rows claim the same principal.
67 #[error("duplicate subnet principal in catalog: {subnet_principal}")]
68 DuplicateSubnet {
69 /// Repeated Subnet principal.
70 subnet_principal: String,
71 },
72
73 /// Subnet rows are not strictly ordered by canonical principal text.
74 #[error("noncanonical subnet order: {previous} must sort before {current}")]
75 NonCanonicalSubnetOrder {
76 /// Previous Subnet principal.
77 previous: String,
78 /// Current Subnet principal.
79 current: String,
80 },
81
82 /// A routing range points to a Subnet absent from the catalog.
83 #[error("routing range references unknown subnet: {subnet_principal}")]
84 UnknownRoutingSubnet {
85 /// Target Subnet principal missing its record.
86 subnet_principal: String,
87 },
88
89 /// A routing range's principal-byte boundaries are reversed.
90 #[error(
91 "invalid routing range for {subnet_principal}: start {start_canister_id} sorts after end {end_canister_id}"
92 )]
93 InvalidRoutingRange {
94 /// Subnet to which this range routes.
95 subnet_principal: String,
96 /// Inclusive start principal of the rejected range.
97 start_canister_id: String,
98 /// Inclusive end principal of the rejected range.
99 end_canister_id: String,
100 },
101
102 /// Two inclusive routing ranges cover a common principal.
103 #[error("overlapping routing ranges: {first} overlaps {second}")]
104 OverlappingRoutingRanges {
105 /// Earlier range in canonical routing order.
106 first: Box<RoutingRange>,
107 /// Later range that overlaps the earlier range.
108 second: Box<RoutingRange>,
109 },
110
111 /// Routing ranges are not strictly ordered by principal bytes and target.
112 #[error("noncanonical routing range order: {previous} must sort before {current}")]
113 NonCanonicalRoutingOrder {
114 /// Previous routing range.
115 previous: Box<RoutingRange>,
116 /// Current routing range.
117 current: Box<RoutingRange>,
118 },
119
120 /// Derived Subnet kind contradicts its raw Registry numeric code.
121 #[error(
122 "subnet {subnet_principal} kind {actual} contradicts Registry subnet_type={registry_subnet_type}; expected {expected}"
123 )]
124 SubnetKindMismatch {
125 /// Subnet principal.
126 subnet_principal: String,
127 /// Raw Registry numeric discriminant.
128 registry_subnet_type: i32,
129 /// Kind required by the raw discriminant.
130 expected: String,
131 /// Kind supplied by the raw catalog.
132 actual: String,
133 },
134
135 /// Charging default contradicts the raw Registry Subnet kind.
136 #[error(
137 "subnet {subnet_principal} charges_apply_by_default={actual} contradicts expected {expected}"
138 )]
139 ChargingPolicyMismatch {
140 /// Subnet principal.
141 subnet_principal: String,
142 /// Charging default required by the raw kind.
143 expected: bool,
144 /// Charging default supplied by the raw catalog.
145 actual: bool,
146 },
147
148 /// Classification metadata contradicts its policy or source.
149 #[error("subnet {subnet_principal} classification field {field} is invalid: {reason}")]
150 ClassificationMismatch {
151 /// Subnet principal.
152 subnet_principal: String,
153 /// Invalid classification field.
154 field: &'static str,
155 /// Deterministic mismatch reason.
156 reason: String,
157 },
158
159 /// A provenance timestamp is not valid canonical UTC text.
160 #[error("invalid catalog timestamp in {field}: {value:?}")]
161 InvalidTimestamp {
162 /// Timestamp field.
163 field: &'static str,
164 /// Invalid timestamp text.
165 value: String,
166 },
167
168 /// A provenance timestamp is beyond the caller's future-skew policy.
169 #[error(
170 "catalog timestamp in {field} is in the future: {value}; latest accepted unix time is {latest_allowed_unix_secs}"
171 )]
172 FutureTimestamp {
173 /// Timestamp field.
174 field: &'static str,
175 /// Future timestamp text.
176 value: String,
177 /// Latest accepted Unix time.
178 latest_allowed_unix_secs: u64,
179 },
180
181 /// A source endpoint is not a clean credential-free HTTP(S) base URL.
182 #[error("invalid catalog source endpoint {endpoint:?}: {reason}")]
183 InvalidSourceEndpoint {
184 /// Invalid endpoint.
185 endpoint: String,
186 /// Endpoint validation reason.
187 reason: String,
188 },
189
190 /// Provenance evidence is internally inconsistent.
191 #[error("invalid catalog provenance field {field}: {reason}")]
192 InvalidProvenance {
193 /// Invalid provenance field.
194 field: &'static str,
195 /// Deterministic validation reason.
196 reason: String,
197 },
198
199 /// Serialized raw evidence cannot establish the claimed assurance level.
200 #[error(
201 "unsupported catalog assurance {assurance} for raw validation; certified authority requires a sealed authenticated Registry archive"
202 )]
203 UnsupportedAssurance {
204 /// Unsupported assurance label.
205 assurance: String,
206 },
207
208 /// Classification policy schema is not supported by this build.
209 #[error("classification policy schema {found} is unsupported; expected {supported}")]
210 ClassificationPolicyVersionMismatch {
211 /// Catalog policy schema.
212 found: u32,
213 /// Supported policy schema.
214 supported: u32,
215 },
216
217 /// Classification policy digest does not match this build.
218 #[error("classification policy digest mismatch: found {actual}; expected {expected}")]
219 ClassificationPolicyDigestMismatch {
220 /// Expected policy digest.
221 expected: String,
222 /// Catalog policy digest.
223 actual: String,
224 },
225
226 /// Resolver implementation identity does not match this build.
227 #[error(
228 "resolver policy mismatch: version {actual_version} backend {actual_backend:?}; expected version {expected_version} backend {expected_backend:?}"
229 )]
230 ResolverPolicyMismatch {
231 /// Expected resolver schema.
232 expected_version: u32,
233 /// Catalog resolver schema.
234 actual_version: u32,
235 /// Expected resolver backend.
236 expected_backend: String,
237 /// Catalog resolver backend.
238 actual_backend: String,
239 },
240
241 /// Catalog digest is not exactly 32 lowercase hexadecimal bytes.
242 #[error("invalid catalog digest {value:?}; expected 64 lowercase hexadecimal characters")]
243 InvalidCatalogDigest {
244 /// Invalid digest text.
245 value: String,
246 },
247
248 /// Agreement digest is absent or not exactly 32 lowercase hexadecimal bytes.
249 #[error(
250 "invalid catalog agreement digest {value:?}; expected 64 lowercase hexadecimal characters"
251 )]
252 InvalidAgreementDigest {
253 /// Invalid digest text.
254 value: String,
255 },
256
257 /// Stored agreement digest does not match the canonical Registry payload.
258 #[error("catalog agreement digest mismatch: found {actual}; expected {expected}")]
259 AgreementDigestMismatch {
260 /// Recomputed agreement digest.
261 expected: String,
262 /// Stored agreement digest.
263 actual: String,
264 },
265
266 /// Stored catalog digest does not match the canonical authority payload.
267 #[error("catalog digest mismatch: found {actual}; expected {expected}")]
268 CatalogDigestMismatch {
269 /// Recomputed digest.
270 expected: String,
271 /// Stored digest.
272 actual: String,
273 },
274
275 /// An exact Subnet lookup did not match a catalog row.
276 #[error("subnet principal {subnet_principal} was not found in the cached catalog")]
277 UnknownSubnet {
278 /// Requested Subnet principal.
279 subnet_principal: String,
280 },
281
282 /// A Subnet principal prefix has no catalog match.
283 #[error("principal prefix {prefix:?} did not match cached subnet principals")]
284 PrincipalPrefixNotFound {
285 /// Requested principal prefix.
286 prefix: String,
287 },
288
289 /// A Subnet principal prefix matches more than one catalog row.
290 #[error("principal prefix {prefix:?} is ambiguous; matches: {matches:?}")]
291 AmbiguousPrincipalPrefix {
292 /// Requested principal prefix.
293 prefix: String,
294 /// Canonical Subnet principals matching the prefix.
295 matches: Vec<String>,
296 },
297
298 /// No routing range covers the requested canister at this Registry version.
299 #[error(
300 "canister principal {canister_principal} was not covered by cached routing ranges at registry_version={registry_version}, catalog_schema_version={catalog_schema_version}"
301 )]
302 RouteNotFound {
303 /// Canonical canister principal whose route was requested.
304 canister_principal: String,
305 /// Registry version represented by the catalog.
306 registry_version: u64,
307 /// Schema identifier retained by the catalog.
308 catalog_schema_version: u32,
309 },
310}