ic_query/nns/neuron/report/error.rs
1//! Module: nns::neuron::report::error
2//!
3//! Responsibility: expose portable and native-wrapper NNS neuron failures.
4//! Does not own: transport execution, cache mechanics, or process presentation.
5//! Boundary: keeps canister and custom callers independent of native host dependencies.
6
7use crate::nns::governance::{NnsGovernanceError, NnsGovernanceSourceProvenance};
8#[cfg(feature = "nns-host")]
9use crate::{
10 HostCacheError, nns::governance::NnsGovernanceAttemptReadError, runtime::RuntimeError,
11};
12#[cfg(feature = "nns-host")]
13use std::path::PathBuf;
14use thiserror::Error as ThisError;
15
16///
17/// NnsNeuronError
18///
19/// Portable failure returned while collecting or assembling neuron reports.
20///
21
22#[derive(Debug, ThisError)]
23pub enum NnsNeuronError {
24 /// Shared Governance request, transport, or provenance validation failed.
25 #[error(transparent)]
26 Governance(#[from] NnsGovernanceError),
27
28 /// The requested page size is outside Governance's supported range.
29 #[error("invalid NNS neuron page size {page_size}; expected 1..={max_page_size}")]
30 InvalidPageSize {
31 /// Rejected page size.
32 page_size: u32,
33 /// Largest page supported by Governance.
34 max_page_size: u32,
35 },
36
37 /// Governance returned its typed application-level error.
38 #[error("NNS Governance rejected the neuron query with code {error_type}: {message}")]
39 GovernanceResponse {
40 /// Raw Governance error type.
41 error_type: i32,
42 /// Governance error message.
43 message: String,
44 },
45
46 /// Governance has no publicly readable view for the requested neuron id.
47 #[error("NNS neuron {neuron_id} was not found")]
48 NeuronNotFound {
49 /// Requested neuron identifier.
50 neuron_id: u64,
51 },
52
53 /// Governance returned a neuron row without its required identifier.
54 #[error("NNS Governance returned a neuron row without an id")]
55 MissingNeuronId,
56
57 /// A source page or detail row violated the neuron contract.
58 #[error("invalid NNS neuron response: {reason}")]
59 InvalidResponse {
60 /// Response invariant that failed.
61 reason: String,
62 },
63
64 /// A resumable collection was configured without any page capacity.
65 #[error("invalid NNS neuron collection page limit 0; expected at least one page")]
66 InvalidCollectionMaxPages,
67
68 /// Serialized or caller-modified resumable collection state is inconsistent.
69 #[error("invalid NNS neuron collection state: {reason}")]
70 InvalidCollectionState {
71 /// Deterministic state invariant that failed.
72 reason: String,
73 },
74
75 /// A continuation request changed an identity fixed when collection started.
76 #[error(
77 "NNS neuron collection request changed {field}: received {actual}, expected {expected}"
78 )]
79 CollectionRequestMismatch {
80 /// Fixed request field that changed.
81 field: &'static str,
82 /// Value retained by the collection state.
83 expected: String,
84 /// Value supplied by the continuation request.
85 actual: String,
86 },
87
88 /// A caller attempted to advance a collection that already exhausted the API.
89 #[error("NNS neuron collection is already complete after {pages_fetched} pages")]
90 CollectionComplete {
91 /// Pages admitted before API exhaustion.
92 pages_fetched: u32,
93 },
94
95 /// A caller attempted to advance a collection after consuming its page budget.
96 #[error("NNS neuron collection reached its {max_pages}-page limit after {pages_fetched} pages")]
97 CollectionPageLimitReached {
98 /// Pages admitted before the stopped advance.
99 pages_fetched: u32,
100 /// Configured cumulative page ceiling.
101 max_pages: u32,
102 },
103
104 /// A later collection page was returned by a different collector.
105 #[error("NNS neuron collection source changed: received {actual:?}, expected {expected:?}")]
106 CollectionSourceChanged {
107 /// Concrete source retained from the first admitted page.
108 expected: NnsGovernanceSourceProvenance,
109 /// Concrete source returned with the candidate page.
110 actual: NnsGovernanceSourceProvenance,
111 },
112
113 /// Cumulative page or row accounting exceeded its integer representation.
114 #[error("NNS neuron collection accounting overflow")]
115 CollectionAccountingOverflow,
116}
117
118///
119/// NnsNeuronHostError
120///
121/// Native wrapper failure for neuron live calls, caches, and refreshes.
122///
123
124#[cfg(feature = "nns-host")]
125#[derive(Debug, ThisError)]
126pub enum NnsNeuronHostError {
127 /// Portable neuron collection or validation failed.
128 #[error(transparent)]
129 Neuron(#[from] NnsNeuronError),
130
131 /// A capped or stalled refresh stopped before proving API exhaustion.
132 #[error(
133 "NNS neuron refresh stopped after {pages_fetched} pages and {rows_fetched} rows: {reason}"
134 )]
135 IncompleteRefresh {
136 /// Pages retained before the stop.
137 pages_fetched: u32,
138 /// Rows retained before the stop.
139 rows_fetched: usize,
140 /// Completion invariant that failed.
141 reason: String,
142 },
143
144 /// A stored neuron snapshot did not match its cache key.
145 #[error(
146 "cached NNS neuron snapshot identity mismatch at {}: {field} is {actual}, expected {expected}",
147 path.display()
148 )]
149 CacheIdentityMismatch {
150 /// Cache path being validated.
151 path: PathBuf,
152 /// Identity field that did not match.
153 field: &'static str,
154 /// Identity required by the cache key.
155 expected: String,
156 /// Identity stored in the snapshot.
157 actual: String,
158 },
159
160 /// A stored neuron snapshot failed family-specific validation.
161 #[error("invalid NNS neuron cache at {}: {reason}", path.display())]
162 InvalidCache {
163 /// Cache path being validated.
164 path: PathBuf,
165 /// Cache invariant that failed.
166 reason: String,
167 },
168
169 /// A stored refresh-attempt sidecar failed identity or lifecycle validation.
170 #[error("invalid NNS neuron refresh attempt at {}: {reason}", path.display())]
171 InvalidRefreshAttempt {
172 /// Attempt sidecar path being validated.
173 path: PathBuf,
174 /// Attempt invariant that failed.
175 reason: String,
176 },
177
178 /// Shared cache IO or lock handling failed.
179 #[error(transparent)]
180 Cache(#[from] HostCacheError),
181
182 /// The synchronous host runtime could not execute the live query.
183 #[error(transparent)]
184 Runtime(#[from] RuntimeError),
185}
186
187#[cfg(feature = "nns-host")]
188impl From<NnsGovernanceAttemptReadError> for NnsNeuronHostError {
189 fn from(error: NnsGovernanceAttemptReadError) -> Self {
190 match error {
191 NnsGovernanceAttemptReadError::Cache(error) => Self::Cache(error),
192 NnsGovernanceAttemptReadError::Invalid { path, reason } => {
193 Self::InvalidRefreshAttempt { path, reason }
194 }
195 }
196 }
197}