sui_indexer/
errors.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use fastcrypto::error::FastCryptoError;
5use jsonrpsee::types::ErrorObjectOwned as RpcError;
6use sui_name_service::NameServiceError;
7use thiserror::Error;
8
9use sui_types::base_types::ObjectIDParseError;
10use sui_types::error::{SuiError, SuiObjectResponseError, UserInputError};
11
12#[derive(Debug, Error)]
13pub struct DataDownloadError {
14    pub error: IndexerError,
15    pub next_checkpoint_sequence_number: u64,
16}
17
18impl std::fmt::Display for DataDownloadError {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        write!(
21            f,
22            "next_checkpoint_seq: {}, error: {}",
23            self.next_checkpoint_sequence_number, self.error
24        )
25    }
26}
27
28#[derive(Debug, Error)]
29pub enum IndexerError {
30    #[error("Indexer failed to read from archives store with error: `{0}`")]
31    ArchiveReaderError(String),
32
33    #[error("Stream closed unexpectedly with error: `{0}`")]
34    ChannelClosed(String),
35
36    #[error("Indexer failed to convert timestamp to NaiveDateTime with error: `{0}`")]
37    DateTimeParsingError(String),
38
39    #[error("Indexer failed to deserialize event from events table with error: `{0}`")]
40    EventDeserializationError(String),
41
42    #[error(
43        "Fullnode returns unexpected responses, which may block indexers from proceeding, with error: `{0}`"
44    )]
45    UnexpectedFullnodeResponseError(String),
46
47    #[error("Indexer failed to transform data with error: `{0}`")]
48    DataTransformationError(String),
49
50    #[error("Indexer failed to read fullnode with error: `{0}`")]
51    FullNodeReadingError(String),
52
53    #[error("Indexer failed to convert structs to diesel Insertable with error: `{0}`")]
54    InsertableParsingError(String),
55
56    #[error("Indexer failed to build JsonRpcServer with error: `{0}`")]
57    JsonRpcServerError(#[from] sui_json_rpc::error::Error),
58
59    #[error("Indexer failed to find object mutations, which should never happen.")]
60    ObjectMutationNotAvailable,
61
62    #[error("Indexer failed to build PG connection pool with error: `{0}`")]
63    PgConnectionPoolInitError(String),
64
65    #[error("Indexer failed to get a pool connection from PG connection pool with error: `{0}`")]
66    PgPoolConnectionError(String),
67
68    #[error("Indexer failed to read PostgresDB with error: `{0}`")]
69    PostgresReadError(String),
70
71    #[error("Indexer failed to reset PostgresDB with error: `{0}`")]
72    PostgresResetError(String),
73
74    #[error("Indexer failed to commit changes to PostgresDB with error: `{0}`")]
75    PostgresWriteError(String),
76
77    #[error(transparent)]
78    PostgresError(#[from] diesel::result::Error),
79
80    #[error("Indexer failed to initialize fullnode Http client with error: `{0}`")]
81    HttpClientInitError(String),
82
83    #[error("Indexer failed to serialize/deserialize with error: `{0}`")]
84    SerdeError(String),
85
86    #[error("Indexer error related to dynamic field: `{0}`")]
87    DynamicFieldError(String),
88
89    #[error("Indexer does not support the feature with error: `{0}`")]
90    NotSupportedError(String),
91
92    #[error("Indexer read corrupted/incompatible data from persistent storage: `{0}`")]
93    PersistentStorageDataCorruptionError(String),
94
95    #[error("Indexer generic error: `{0}`")]
96    GenericError(String),
97
98    #[error("GCS error: `{0}`")]
99    GcsError(String),
100
101    #[error("Indexer failed to resolve object to move struct with error: `{0}`")]
102    ResolveMoveStructError(String),
103
104    #[error(transparent)]
105    UncategorizedError(#[from] anyhow::Error),
106
107    #[error(transparent)]
108    ObjectIdParseError(#[from] ObjectIDParseError),
109
110    #[error("Invalid transaction digest with error: `{0}`")]
111    InvalidTransactionDigestError(String),
112
113    #[error(transparent)]
114    SuiError(#[from] SuiError),
115
116    #[error(transparent)]
117    BcsError(#[from] bcs::Error),
118
119    #[error("Invalid argument with error: `{0}`")]
120    InvalidArgumentError(String),
121
122    #[error(transparent)]
123    UserInputError(#[from] UserInputError),
124
125    #[error("Indexer failed to resolve module with error: `{0}`")]
126    ModuleResolutionError(String),
127
128    #[error(transparent)]
129    ObjectResponseError(#[from] SuiObjectResponseError),
130
131    #[error(transparent)]
132    FastCryptoError(#[from] FastCryptoError),
133
134    #[error("`{0}`: `{1}`")]
135    ErrorWithContext(String, Box<IndexerError>),
136
137    #[error("Indexer failed to send item to channel with error: `{0}`")]
138    MpscChannelError(String),
139
140    #[error(transparent)]
141    NameServiceError(#[from] NameServiceError),
142
143    #[error("Inconsistent migration records: {0}")]
144    DbMigrationError(String),
145}
146
147pub trait Context<T> {
148    fn context(self, context: &str) -> Result<T, IndexerError>;
149}
150
151impl<T> Context<T> for Result<T, IndexerError> {
152    fn context(self, context: &str) -> Result<T, IndexerError> {
153        self.map_err(|e| IndexerError::ErrorWithContext(context.to_string(), Box::new(e)))
154    }
155}
156
157impl From<IndexerError> for RpcError {
158    fn from(e: IndexerError) -> Self {
159        RpcError::owned(
160            jsonrpsee::types::error::CALL_EXECUTION_FAILED_CODE,
161            e.to_string(),
162            None::<()>,
163        )
164    }
165}
166
167impl From<tokio::task::JoinError> for IndexerError {
168    fn from(value: tokio::task::JoinError) -> Self {
169        IndexerError::UncategorizedError(anyhow::Error::from(value))
170    }
171}
172
173impl From<diesel_async::pooled_connection::bb8::RunError> for IndexerError {
174    fn from(value: diesel_async::pooled_connection::bb8::RunError) -> Self {
175        Self::PgPoolConnectionError(value.to_string())
176    }
177}
178
179impl From<sui_types::error::SuiErrorKind> for IndexerError {
180    fn from(e: sui_types::error::SuiErrorKind) -> Self {
181        IndexerError::SuiError(SuiError::from(e))
182    }
183}
184
185pub(crate) fn client_error_to_error_object(
186    e: jsonrpsee::core::ClientError,
187) -> jsonrpsee::types::ErrorObjectOwned {
188    match e {
189        jsonrpsee::core::ClientError::Call(e) => e,
190        _ => jsonrpsee::types::ErrorObjectOwned::owned(
191            jsonrpsee::types::error::UNKNOWN_ERROR_CODE,
192            e.to_string(),
193            None::<()>,
194        ),
195    }
196}