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_stages_types::StageId;
10use reth_static_file_types::StaticFileSegment;
11use thiserror::Error;
12use tokio::sync::broadcast::error::SendError;
13
14#[derive(Error, Debug)]
16pub enum BlockErrorKind {
17 #[error("validation error: {0}")]
19 Validation(#[from] ConsensusError),
20 #[error("execution error: {0}")]
22 Execution(#[from] BlockExecutionError),
23}
24
25impl BlockErrorKind {
26 pub const fn is_state_root_error(&self) -> bool {
28 matches!(self, Self::Validation(err) if err.is_state_root_error())
29 }
30}
31
32#[derive(Error, Debug)]
34pub enum StageError {
35 #[error("stage encountered an error in block #{number}: {error}", number = block.block.number)]
37 Block {
38 block: Box<BlockWithParent>,
40 #[source]
42 error: BlockErrorKind,
43 },
44 #[error(
47 "stage encountered inconsistent chain: \
48 downloaded header #{header_number} ({header_hash}) is detached from \
49 local head #{head_number} ({head_hash}): {error}",
50 header_number = header.block.number,
51 header_hash = header.block.hash,
52 head_number = local_head.block.number,
53 head_hash = local_head.block.hash,
54 )]
55 DetachedHead {
56 local_head: Box<BlockWithParent>,
58 header: Box<BlockWithParent>,
60 #[source]
62 error: Box<ConsensusError>,
63 },
64 #[error("missing sync gap")]
66 MissingSyncGap,
67 #[error("internal database error occurred: {0}")]
69 Database(#[from] DatabaseError),
70 #[error(transparent)]
72 PruningConfiguration(#[from] PruneSegmentError),
73 #[error(transparent)]
75 Pruner(#[from] PrunerError),
76 #[error("invalid stage checkpoint: {0}")]
78 StageCheckpoint(u64),
79 #[error("missing download buffer")]
82 MissingDownloadBuffer,
83 #[error("download channel closed")]
85 ChannelClosed,
86 #[error("database integrity error occurred: {0}")]
88 DatabaseIntegrity(#[from] ProviderError),
89 #[error("invalid download response: {0}")]
92 Download(#[from] DownloadError),
93 #[error("missing static file data for block number: {number}", number = block.block.number)]
95 MissingStaticFileData {
96 block: Box<BlockWithParent>,
98 segment: StaticFileSegment,
100 },
101 #[error("missing prune checkpoint for {0}")]
103 MissingPruneCheckpoint(PruneSegment),
104 #[error("post execute commit error occurred: {_0}")]
106 PostExecuteCommit(&'static str),
107 #[error(transparent)]
109 Internal(#[from] RethError),
110 #[error(transparent)]
115 Recoverable(Box<dyn core::error::Error + Send + Sync>),
116 #[error(transparent)]
120 Fatal(Box<dyn core::error::Error + Send + Sync>),
121}
122
123impl StageError {
124 pub const fn is_fatal(&self) -> bool {
126 matches!(
127 self,
128 Self::Database(_) |
129 Self::Download(_) |
130 Self::DatabaseIntegrity(_) |
131 Self::StageCheckpoint(_) |
132 Self::MissingDownloadBuffer |
133 Self::MissingSyncGap |
134 Self::ChannelClosed |
135 Self::Internal(_) |
136 Self::Fatal(_)
137 )
138 }
139}
140
141impl From<std::io::Error> for StageError {
142 fn from(source: std::io::Error) -> Self {
143 Self::Fatal(Box::new(source))
144 }
145}
146
147impl From<DecompressError> for StageError {
148 fn from(error: DecompressError) -> Self {
149 Self::Database(DatabaseError::from(error))
150 }
151}
152
153#[derive(Error, Debug)]
155pub enum PipelineError {
156 #[error(transparent)]
158 Stage(#[from] StageError),
159 #[error(transparent)]
161 Database(#[from] DatabaseError),
162 #[error(transparent)]
164 Provider(#[from] ProviderError),
165 #[error("pipeline encountered an error while trying to send an event")]
167 Channel(#[from] Box<SendError<PipelineEvent>>),
168 #[error(transparent)]
170 Internal(#[from] RethError),
171 #[error("unexpected unwind")]
173 UnexpectedUnwind,
174 #[error(transparent)]
176 UnwindTargetPruned(#[from] UnwindTargetPrunedError),
177 #[error("pipeline does not contain stage {0}")]
179 MissingStage(StageId),
180}