Skip to main content

frame_state/
error.rs

1use std::error::Error;
2use std::fmt;
3
4use frame_core::capability::{CapabilityCheckError, CapabilityDenied};
5use frame_core::component::ComponentId;
6use haematite::{
7    BranchCommitError, BranchError, BranchKind, BranchPolicyError, BranchRefError, CheckoutError,
8    MergeError, NodeError, SnapshotError, StoreError, TreeError,
9};
10
11use crate::types::{MergePolicy, ReferenceTargetState};
12
13/// A durable branch/meta mismatch that boot reconciliation refuses to guess at.
14#[derive(Debug)]
15pub enum ReconcileInconsistency {
16    /// A component Namespace ref exists with no corresponding meta record.
17    BranchWithoutMeta {
18        /// Durable branch name observed.
19        branch: String,
20        /// Persisted kind observed.
21        kind: BranchKind,
22    },
23    /// Meta says Active but the required namespace ref is absent.
24    ActiveWithoutBranch {
25        /// Component named by the durable Active record.
26        component: ComponentId,
27    },
28    /// Meta says Archived but a same-named namespace ref is present.
29    ArchivedWithBranch {
30        /// Component named by both conflicting durable records.
31        component: ComponentId,
32    },
33    /// A removed namespace has no deterministic archive snapshot.
34    RemovedWithoutSnapshot {
35        /// Component whose branch was removed.
36        component: ComponentId,
37        /// Snapshot name that must have been pinned first.
38        snapshot: String,
39    },
40    /// A deterministic snapshot name points at a different root.
41    SnapshotRootMismatch {
42        /// Snapshot name whose existing binding was inspected.
43        snapshot: String,
44        /// Root required by the archive transition.
45        expected: haematite::Hash,
46        /// Root already bound under that name.
47        actual: haematite::Hash,
48    },
49}
50
51impl fmt::Display for ReconcileInconsistency {
52    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match self {
54            Self::BranchWithoutMeta { branch, kind } => write!(
55                formatter,
56                "durable branch '{branch}' ({kind}) has no frame-state meta record"
57            ),
58            Self::ActiveWithoutBranch { component } => {
59                write!(
60                    formatter,
61                    "active component {component} has no Namespace branch"
62                )
63            }
64            Self::ArchivedWithBranch { component } => write!(
65                formatter,
66                "archived component {component} still has a Namespace branch"
67            ),
68            Self::RemovedWithoutSnapshot {
69                component,
70                snapshot,
71            } => write!(
72                formatter,
73                "component {component} branch was removed without required snapshot '{snapshot}'"
74            ),
75            Self::SnapshotRootMismatch {
76                snapshot,
77                expected,
78                actual,
79            } => write!(
80                formatter,
81                "snapshot '{snapshot}' names root {actual}, expected {expected}"
82            ),
83        }
84    }
85}
86
87impl Error for ReconcileInconsistency {}
88
89/// Typed failures from component storage, preserving every substrate error.
90#[derive(Debug)]
91pub enum StateError {
92    /// Filesystem setup failed outside a haematite operation.
93    Io(std::io::Error),
94    /// Node-store operation failed.
95    Store(StoreError),
96    /// Empty-node construction failed.
97    Node(NodeError),
98    /// Tree traversal failed.
99    Tree(TreeError),
100    /// Branch commit/lifecycle failed outside a more specific category.
101    BranchCommit(BranchCommitError),
102    /// Engine namespace policy refused an operation, retaining names and kinds.
103    BranchPolicy(BranchPolicyError),
104    /// Engine content merge failed.
105    Merge(MergeError),
106    /// Durable branch-ref operation failed.
107    BranchRef(BranchRefError),
108    /// Branch handle operation failed.
109    Branch(BranchError),
110    /// Snapshot registry operation failed.
111    Snapshot(SnapshotError),
112    /// Historical checkout failed.
113    Checkout(CheckoutError),
114    /// The supplied checker belongs to a different component than the handle.
115    CapabilityBinding {
116        /// Component incarnation represented by the storage handle.
117        handle_component: ComponentId,
118        /// Component whose authority the supplied checker would evaluate.
119        checker_component: ComponentId,
120    },
121    /// A fresh check denied this resolution, preserved verbatim from frame-core.
122    CapabilityDenied(CapabilityDenied),
123    /// A fresh check could not produce or publish its verdict.
124    CapabilityCheck(CapabilityCheckError),
125    /// The named target has no active namespace to resolve.
126    DanglingReference {
127        /// Component named by the link.
128        target: ComponentId,
129        /// Durable target state observed after authority was granted.
130        state: ReferenceTargetState,
131    },
132    /// Internal synchronization was poisoned by a prior panic.
133    SynchronizationPoisoned,
134    /// No schema was declared before install.
135    SchemaNotDeclared {
136        /// Component whose install lacks a declaration.
137        component: ComponentId,
138    },
139    /// A schema was already fixed for this component's current lifecycle.
140    SchemaAlreadyDeclared {
141        /// Component whose pending declaration would be replaced.
142        component: ComponentId,
143    },
144    /// The published engine cannot execute the declared vocabulary entry.
145    UnsupportedPolicy {
146        /// Policy refused at schema declaration.
147        policy: MergePolicy,
148    },
149    /// A custom policy named a resolver absent at declaration time.
150    ResolverNotRegistered {
151        /// Stable resolver name that was not registered.
152        name: String,
153    },
154    /// A resolver registration would silently replace an existing function.
155    ResolverAlreadyRegistered {
156        /// Stable resolver name already bound in this process.
157        name: String,
158    },
159    /// An install is already executing for this identity.
160    InstallInProgress {
161        /// Component with an active install transition.
162        component: ComponentId,
163    },
164    /// An archive is already executing for this identity.
165    ArchiveInProgress {
166        /// Component with an active archive transition.
167        component: ComponentId,
168    },
169    /// A fork or merge is already executing for this identity.
170    WorkInProgress {
171        /// Component with an active Work operation.
172        component: ComponentId,
173    },
174    /// Install was requested for an active namespace.
175    AlreadyActive {
176        /// Component whose namespace is already Active.
177        component: ComponentId,
178    },
179    /// The component has no active namespace.
180    NotActive {
181        /// Component for which active storage was required.
182        component: ComponentId,
183    },
184    /// A handle's captured incarnation no longer owns the branch name.
185    StaleHandle {
186        /// Component whose name was reused.
187        component: ComponentId,
188        /// Incarnation captured by the handle.
189        held: u64,
190        /// Durable current incarnation.
191        current: u64,
192    },
193    /// A requested Work branch is not outstanding.
194    WorkNotFound {
195        /// Durable Work branch name that was absent.
196        branch: String,
197    },
198    /// A Work handle came from another store, component, or incarnation.
199    ForeignWorkHandle,
200    /// A branch/archive key did not have Frame's fixed-width encoding.
201    CorruptRecord {
202        /// Exact codec or durable-invariant failure.
203        detail: String,
204    },
205    /// Boot found a durable state it cannot safely repair.
206    Reconcile(ReconcileInconsistency),
207}
208
209impl fmt::Display for StateError {
210    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
211        match self {
212            Self::Io(error) => write!(formatter, "frame-state filesystem error: {error}"),
213            Self::Store(error) => write!(formatter, "frame-state node store error: {error}"),
214            Self::Node(error) => write!(formatter, "frame-state node encoding error: {error}"),
215            Self::Tree(error) => write!(formatter, "frame-state tree traversal error: {error}"),
216            Self::BranchCommit(error) => error.fmt(formatter),
217            Self::BranchPolicy(error) => error.fmt(formatter),
218            Self::Merge(error) => error.fmt(formatter),
219            Self::BranchRef(error) => error.fmt(formatter),
220            Self::Branch(error) => error.fmt(formatter),
221            Self::Snapshot(error) => error.fmt(formatter),
222            Self::Checkout(error) => error.fmt(formatter),
223            Self::CapabilityBinding {
224                handle_component,
225                checker_component,
226            } => fmt_capability_binding(formatter, *handle_component, *checker_component),
227            Self::CapabilityDenied(denial) => fmt_capability_denied(formatter, denial),
228            Self::CapabilityCheck(error) => error.fmt(formatter),
229            Self::DanglingReference { target, state } => {
230                fmt_dangling_reference(formatter, *target, *state)
231            }
232            Self::SynchronizationPoisoned => {
233                formatter.write_str("frame-state synchronization poisoned")
234            }
235            Self::SchemaNotDeclared { component } => {
236                write!(
237                    formatter,
238                    "component {component} has no declared storage schema"
239                )
240            }
241            Self::SchemaAlreadyDeclared { component } => {
242                write!(
243                    formatter,
244                    "component {component} storage schema is already declared"
245                )
246            }
247            Self::UnsupportedPolicy { policy } => {
248                write!(
249                    formatter,
250                    "haematite 0.5.0 cannot execute merge policy {policy}"
251                )
252            }
253            Self::ResolverNotRegistered { name } => {
254                write!(
255                    formatter,
256                    "custom merge resolver '{name}' is not registered"
257                )
258            }
259            Self::ResolverAlreadyRegistered { name } => {
260                write!(
261                    formatter,
262                    "custom merge resolver '{name}' is already registered"
263                )
264            }
265            Self::InstallInProgress { component } => {
266                write!(
267                    formatter,
268                    "component {component} storage install is in progress"
269                )
270            }
271            Self::ArchiveInProgress { component } => {
272                write!(
273                    formatter,
274                    "component {component} storage archive is in progress"
275                )
276            }
277            Self::WorkInProgress { component } => {
278                write!(
279                    formatter,
280                    "component {component} Work operation is in progress"
281                )
282            }
283            Self::AlreadyActive { component } => {
284                write!(formatter, "component {component} storage is already active")
285            }
286            Self::NotActive { component } => {
287                write!(formatter, "component {component} storage is not active")
288            }
289            Self::StaleHandle {
290                component,
291                held,
292                current,
293            } => write!(
294                formatter,
295                "stale handle for component {component}: held incarnation {held}, current incarnation {current}"
296            ),
297            Self::WorkNotFound { branch } => {
298                write!(formatter, "Work branch '{branch}' is not outstanding")
299            }
300            Self::ForeignWorkHandle => formatter
301                .write_str("Work handle belongs to another store, component, or incarnation"),
302            Self::CorruptRecord { detail } => {
303                write!(formatter, "corrupt frame-state record: {detail}")
304            }
305            Self::Reconcile(error) => error.fmt(formatter),
306        }
307    }
308}
309
310fn fmt_capability_binding(
311    formatter: &mut fmt::Formatter<'_>,
312    handle_component: ComponentId,
313    checker_component: ComponentId,
314) -> fmt::Result {
315    write!(
316        formatter,
317        "capability checker for component {checker_component} cannot resolve through component {handle_component}'s storage handle"
318    )
319}
320
321fn fmt_capability_denied(
322    formatter: &mut fmt::Formatter<'_>,
323    denial: &CapabilityDenied,
324) -> fmt::Result {
325    write!(
326        formatter,
327        "cross-component resolution denied for component {}: kind {:?}, scope {:?}, declared {}",
328        denial.component_id, denial.kind, denial.scope, denial.declared
329    )
330}
331
332fn fmt_dangling_reference(
333    formatter: &mut fmt::Formatter<'_>,
334    target: ComponentId,
335    state: ReferenceTargetState,
336) -> fmt::Result {
337    write!(
338        formatter,
339        "cross-component reference target {target} is {state}"
340    )
341}
342
343impl Error for StateError {
344    fn source(&self) -> Option<&(dyn Error + 'static)> {
345        match self {
346            Self::Io(error) => Some(error),
347            Self::Store(error) => Some(error),
348            Self::Node(error) => Some(error),
349            Self::Tree(error) => Some(error),
350            Self::BranchCommit(error) => Some(error),
351            Self::BranchPolicy(error) => Some(error),
352            Self::Merge(error) => Some(error),
353            Self::BranchRef(error) => Some(error),
354            Self::Branch(error) => Some(error),
355            Self::Snapshot(error) => Some(error),
356            Self::Checkout(error) => Some(error),
357            Self::CapabilityCheck(error) => Some(error),
358            Self::Reconcile(error) => Some(error),
359            _ => None,
360        }
361    }
362}
363
364impl From<std::io::Error> for StateError {
365    fn from(error: std::io::Error) -> Self {
366        Self::Io(error)
367    }
368}
369impl From<StoreError> for StateError {
370    fn from(error: StoreError) -> Self {
371        Self::Store(error)
372    }
373}
374impl From<NodeError> for StateError {
375    fn from(error: NodeError) -> Self {
376        Self::Node(error)
377    }
378}
379impl From<TreeError> for StateError {
380    fn from(error: TreeError) -> Self {
381        Self::Tree(error)
382    }
383}
384impl From<BranchRefError> for StateError {
385    fn from(error: BranchRefError) -> Self {
386        Self::BranchRef(error)
387    }
388}
389impl From<BranchError> for StateError {
390    fn from(error: BranchError) -> Self {
391        Self::Branch(error)
392    }
393}
394impl From<SnapshotError> for StateError {
395    fn from(error: SnapshotError) -> Self {
396        Self::Snapshot(error)
397    }
398}
399impl From<CheckoutError> for StateError {
400    fn from(error: CheckoutError) -> Self {
401        Self::Checkout(error)
402    }
403}
404impl From<CapabilityCheckError> for StateError {
405    fn from(error: CapabilityCheckError) -> Self {
406        Self::CapabilityCheck(error)
407    }
408}
409impl From<ReconcileInconsistency> for StateError {
410    fn from(error: ReconcileInconsistency) -> Self {
411        Self::Reconcile(error)
412    }
413}
414
415impl From<BranchCommitError> for StateError {
416    fn from(error: BranchCommitError) -> Self {
417        match error {
418            BranchCommitError::Policy(policy) => Self::BranchPolicy(policy),
419            BranchCommitError::Merge(merge) => Self::Merge(merge),
420            other => Self::BranchCommit(other),
421        }
422    }
423}