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