Skip to main content

reth_stages_api/
error.rs

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