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#[derive(Error, Debug)]
14pub enum BlockErrorKind {
15 #[error("validation error: {0}")]
17 Validation(#[from] ConsensusError),
18 #[error("execution error: {0}")]
20 Execution(#[from] BlockExecutionError),
21}
22
23impl BlockErrorKind {
24 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#[derive(Error, Debug)]
35pub enum StageError {
36 #[error("stage encountered an error in block #{number}: {error}", number = block.block.number)]
38 Block {
39 block: Box<BlockWithParent>,
41 #[source]
43 error: BlockErrorKind,
44 },
45 #[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 local_head: Box<BlockWithParent>,
59 header: Box<BlockWithParent>,
61 #[source]
63 error: Box<ConsensusError>,
64 },
65 #[error("missing sync gap")]
67 MissingSyncGap,
68 #[error("internal database error occurred: {0}")]
70 Database(#[from] DatabaseError),
71 #[error(transparent)]
73 PruningConfiguration(#[from] PruneSegmentError),
74 #[error(transparent)]
76 Pruner(#[from] PrunerError),
77 #[error("invalid stage checkpoint: {0}")]
79 StageCheckpoint(u64),
80 #[error("missing download buffer")]
83 MissingDownloadBuffer,
84 #[error("download channel closed")]
86 ChannelClosed,
87 #[error("database integrity error occurred: {0}")]
89 DatabaseIntegrity(#[from] ProviderError),
90 #[error("invalid download response: {0}")]
93 Download(#[from] DownloadError),
94 #[error("missing static file data for block number: {number}", number = block.block.number)]
96 MissingStaticFileData {
97 block: Box<BlockWithParent>,
99 segment: StaticFileSegment,
101 },
102 #[error("missing prune checkpoint for {0}")]
104 MissingPruneCheckpoint(PruneSegment),
105 #[error("post execute commit error occurred: {_0}")]
107 PostExecuteCommit(&'static str),
108 #[error(transparent)]
110 Internal(#[from] RethError),
111 #[error(transparent)]
116 Recoverable(Box<dyn core::error::Error + Send + Sync>),
117 #[error(transparent)]
121 Fatal(Box<dyn core::error::Error + Send + Sync>),
122}
123
124impl StageError {
125 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#[derive(Error, Debug)]
150pub enum PipelineError {
151 #[error(transparent)]
153 Stage(#[from] StageError),
154 #[error(transparent)]
156 Database(#[from] DatabaseError),
157 #[error(transparent)]
159 Provider(#[from] ProviderError),
160 #[error("pipeline encountered an error while trying to send an event")]
162 Channel(#[from] Box<SendError<PipelineEvent>>),
163 #[error(transparent)]
165 Internal(#[from] RethError),
166 #[error("unexpected unwind")]
168 UnexpectedUnwind,
169}