reth_stages_api/
error.rs

1use crate::PipelineEvent;
2use alloy_eips::eip1898::BlockWithParent;
3use reth_consensus::ConsensusError;
4use reth_errors::{BlockExecutionError, DatabaseError, RethError};
5use reth_network_p2p::error::DownloadError;
6use reth_provider::ProviderError;
7use reth_prune::{PruneSegment, PruneSegmentError, PrunerError};
8use reth_static_file_types::StaticFileSegment;
9use thiserror::Error;
10use tokio::sync::broadcast::error::SendError;
11
12/// Represents the specific error type within a block error.
13#[derive(Error, Debug)]
14pub enum BlockErrorKind {
15    /// The block encountered a validation error.
16    #[error("validation error: {0}")]
17    Validation(#[from] ConsensusError),
18    /// The block encountered an execution error.
19    #[error("execution error: {0}")]
20    Execution(#[from] BlockExecutionError),
21}
22
23impl BlockErrorKind {
24    /// Returns `true` if the error is a state root error.
25    pub const fn is_state_root_error(&self) -> bool {
26        match self {
27            Self::Validation(err) => err.is_state_root_error(),
28            Self::Execution(err) => err.is_state_root_error(),
29        }
30    }
31}
32
33/// A stage execution error.
34#[derive(Error, Debug)]
35pub enum StageError {
36    /// The stage encountered an error related to a block.
37    #[error("stage encountered an error in block #{number}: {error}", number = block.block.number)]
38    Block {
39        /// The block that caused the error.
40        block: Box<BlockWithParent>,
41        /// The specific error type, either consensus or execution error.
42        #[source]
43        error: BlockErrorKind,
44    },
45    /// The stage encountered a downloader error where the responses cannot be attached to the
46    /// current head.
47    #[error(
48        "stage encountered inconsistent chain: \
49         downloaded header #{header_number} ({header_hash}) is detached from \
50         local head #{head_number} ({head_hash}): {error}",
51        header_number = header.block.number,
52        header_hash = header.block.hash,
53        head_number = local_head.block.number,
54        head_hash = local_head.block.hash,
55    )]
56    DetachedHead {
57        /// The local head we attempted to attach to.
58        local_head: Box<BlockWithParent>,
59        /// The header we attempted to attach.
60        header: Box<BlockWithParent>,
61        /// The error that occurred when attempting to attach the header.
62        #[source]
63        error: Box<ConsensusError>,
64    },
65    /// The headers stage is missing sync gap.
66    #[error("missing sync gap")]
67    MissingSyncGap,
68    /// The stage encountered a database error.
69    #[error("internal database error occurred: {0}")]
70    Database(#[from] DatabaseError),
71    /// Invalid pruning configuration
72    #[error(transparent)]
73    PruningConfiguration(#[from] PruneSegmentError),
74    /// Pruner error
75    #[error(transparent)]
76    Pruner(#[from] PrunerError),
77    /// Invalid checkpoint passed to the stage
78    #[error("invalid stage checkpoint: {0}")]
79    StageCheckpoint(u64),
80    /// Missing download buffer on stage execution.
81    /// Returned if stage execution was called without polling for readiness.
82    #[error("missing download buffer")]
83    MissingDownloadBuffer,
84    /// Download channel closed
85    #[error("download channel closed")]
86    ChannelClosed,
87    /// The stage encountered a database integrity error.
88    #[error("database integrity error occurred: {0}")]
89    DatabaseIntegrity(#[from] ProviderError),
90    /// Invalid download response. Applicable for stages which
91    /// rely on external downloaders
92    #[error("invalid download response: {0}")]
93    Download(#[from] DownloadError),
94    /// Database is ahead of static file data.
95    #[error("missing static file data for block number: {number}", number = block.block.number)]
96    MissingStaticFileData {
97        /// Starting block with  missing data.
98        block: Box<BlockWithParent>,
99        /// Static File segment
100        segment: StaticFileSegment,
101    },
102    /// The prune checkpoint for the given segment is missing.
103    #[error("missing prune checkpoint for {0}")]
104    MissingPruneCheckpoint(PruneSegment),
105    /// Post Execute Commit error
106    #[error("post execute commit error occurred: {_0}")]
107    PostExecuteCommit(&'static str),
108    /// Internal error
109    #[error(transparent)]
110    Internal(#[from] RethError),
111    /// The stage encountered a recoverable error.
112    ///
113    /// These types of errors are caught by the [Pipeline][crate::Pipeline] and trigger a restart
114    /// of the stage.
115    #[error(transparent)]
116    Recoverable(Box<dyn core::error::Error + Send + Sync>),
117    /// The stage encountered a fatal error.
118    ///
119    /// These types of errors stop the pipeline.
120    #[error(transparent)]
121    Fatal(Box<dyn core::error::Error + Send + Sync>),
122}
123
124impl StageError {
125    /// If the error is fatal the pipeline will stop.
126    pub const fn is_fatal(&self) -> bool {
127        matches!(
128            self,
129            Self::Database(_) |
130                Self::Download(_) |
131                Self::DatabaseIntegrity(_) |
132                Self::StageCheckpoint(_) |
133                Self::MissingDownloadBuffer |
134                Self::MissingSyncGap |
135                Self::ChannelClosed |
136                Self::Internal(_) |
137                Self::Fatal(_)
138        )
139    }
140}
141
142impl From<std::io::Error> for StageError {
143    fn from(source: std::io::Error) -> Self {
144        Self::Fatal(Box::new(source))
145    }
146}
147
148/// A pipeline execution error.
149#[derive(Error, Debug)]
150pub enum PipelineError {
151    /// The pipeline encountered an irrecoverable error in one of the stages.
152    #[error(transparent)]
153    Stage(#[from] StageError),
154    /// The pipeline encountered a database error.
155    #[error(transparent)]
156    Database(#[from] DatabaseError),
157    /// Provider error.
158    #[error(transparent)]
159    Provider(#[from] ProviderError),
160    /// The pipeline encountered an error while trying to send an event.
161    #[error("pipeline encountered an error while trying to send an event")]
162    Channel(#[from] Box<SendError<PipelineEvent>>),
163    /// Internal error
164    #[error(transparent)]
165    Internal(#[from] RethError),
166    /// The pipeline encountered an unwind when `fail_on_unwind` was set to `true`.
167    #[error("unexpected unwind")]
168    UnexpectedUnwind,
169}