Skip to main content

subxt/
error.rs

1// Copyright 2019-2026 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5//! Types representing the errors that can be returned.
6
7mod dispatch_error;
8mod hex;
9
10use std::borrow::Cow;
11use thiserror::Error as DeriveError;
12
13#[cfg(feature = "light-client")]
14pub use subxt_lightclient::LightClientError;
15
16// Re-export dispatch error types:
17pub use dispatch_error::{
18    ArithmeticError, DispatchError, ModuleError, TokenError, TransactionalError,
19};
20
21// Re-expose the errors we use from other crates here:
22pub use hex::Hex;
23pub use scale_decode::Error as DecodeError;
24pub use scale_encode::Error as EncodeError;
25pub use subxt_metadata::Metadata;
26pub use subxt_metadata::TryFromError as MetadataTryFromError;
27
28/// A global error type. Any of the errors exposed here can convert into this
29/// error via `.into()`, but this error isn't itself exposed from anything.
30#[derive(Debug, thiserror::Error)]
31#[non_exhaustive]
32#[allow(missing_docs)]
33pub enum Error {
34    #[error(transparent)]
35    OnlineClientError(#[from] OnlineClientError),
36    #[error(transparent)]
37    OfflineClientAtBlockError(#[from] OfflineClientAtBlockError),
38    #[error(transparent)]
39    OnlineClientAtBlockError(#[from] OnlineClientAtBlockError),
40    #[error(transparent)]
41    ExtrinsicDecodeErrorAt(#[from] ExtrinsicDecodeErrorAt),
42    #[error(transparent)]
43    BlockError(#[from] BlockError),
44    #[error(transparent)]
45    ConstantError(#[from] ConstantError),
46    #[error(transparent)]
47    CustomValueError(#[from] CustomValueError),
48    #[error(transparent)]
49    StorageKeyError(#[from] StorageKeyError),
50    #[error(transparent)]
51    StorageValueError(#[from] StorageValueError),
52    #[error(transparent)]
53    BackendError(#[from] BackendError),
54    #[error(transparent)]
55    BlocksError(#[from] BlocksError),
56    #[error(transparent)]
57    AccountNonceError(#[from] AccountNonceError),
58    #[error(transparent)]
59    RuntimeApiError(#[from] RuntimeApiError),
60    #[error(transparent)]
61    EventsError(#[from] EventsError),
62    #[error(transparent)]
63    ExtrinsicError(#[from] ExtrinsicError),
64    #[error(transparent)]
65    ViewFunctionError(#[from] ViewFunctionError),
66    #[error(transparent)]
67    TransactionProgressError(#[from] TransactionProgressError),
68    #[error(transparent)]
69    TransactionStatusError(#[from] TransactionStatusError),
70    #[error(transparent)]
71    TransactionEventsError(#[from] TransactionEventsError),
72    #[error(transparent)]
73    TransactionFinalizedSuccessError(#[from] TransactionFinalizedSuccessError),
74    #[error(transparent)]
75    ModuleErrorDetailsError(#[from] ModuleErrorDetailsError),
76    #[error(transparent)]
77    ModuleErrorDecodeError(#[from] ModuleErrorDecodeError),
78    #[error(transparent)]
79    DispatchErrorDecodeError(#[from] DispatchErrorDecodeError),
80    #[error(transparent)]
81    StorageError(#[from] StorageError),
82    #[error(transparent)]
83    CombinedBackendError(#[from] CombinedBackendError),
84    // Dev note: Subxt doesn't directly return Raw* errors. These exist so that when
85    // users use common crates (like parity-scale-codec and subxt-rpcs), errors returned
86    // there can be handled automatically using ? when the expected error is subxt::Error.
87    #[error("Other RPC client error: {0}")]
88    OtherRpcClientError(#[from] subxt_rpcs::Error),
89    #[error("Other codec error: {0}")]
90    OtherCodecError(#[from] codec::Error),
91    #[cfg(feature = "light-client")]
92    #[error("Other light client error: {0}")]
93    OtherLightClientError(#[from] subxt_lightclient::LightClientError),
94    #[cfg(feature = "light-client")]
95    #[error("Other light client RPC error: {0}")]
96    OtherLightClientRpcError(#[from] subxt_lightclient::LightClientRpcError),
97    // Dev note: Nothing in subxt should ever emit this error. It can instead be used
98    // to easily map other errors into a subxt::Error for convenience. Some From impls
99    // make this automatic for common "other" error types.
100    #[error("Other error: {0}")]
101    Other(Box<dyn std::error::Error + Send + Sync + 'static>),
102}
103
104impl From<std::convert::Infallible> for Error {
105    fn from(value: std::convert::Infallible) -> Self {
106        match value {}
107    }
108}
109
110impl Error {
111    /// Create a generic error. This is a quick workaround when you are using
112    /// [`Error`] and have a non-Subxt error to return.
113    pub fn other<E: std::error::Error + Send + Sync + 'static>(error: E) -> Error {
114        Error::Other(Box::new(error))
115    }
116
117    /// Create a generic error from a string. This is a quick workaround when you are using
118    /// [`Error`] and have a non-Subxt error to return.
119    pub fn other_str(error: impl Into<String>) -> Error {
120        #[derive(thiserror::Error, Debug, Clone)]
121        #[error("{0}")]
122        struct StrError(String);
123        Error::Other(Box::new(StrError(error.into())))
124    }
125
126    /// Checks whether the error was caused by a RPC re-connection.
127    pub fn is_disconnected_will_reconnect(&self) -> bool {
128        matches!(
129            self.backend_error(),
130            Some(BackendError::Rpc(RpcError::ClientError(
131                subxt_rpcs::Error::DisconnectedWillReconnect(_)
132            )))
133        )
134    }
135
136    /// Checks whether the error was caused by a RPC request being rejected.
137    pub fn is_rpc_limit_reached(&self) -> bool {
138        matches!(
139            self.backend_error(),
140            Some(BackendError::Rpc(RpcError::LimitReached))
141        )
142    }
143
144    fn backend_error(&self) -> Option<&BackendError> {
145        match self {
146            // Many of these contain no backend error, but keep the checks next to
147            // the actual error types to make it harder to miss adding any, and be exhaustive
148            // here so new error variants are not missed as easily.
149            Error::BlocksError(e) => e.backend_error(),
150            Error::AccountNonceError(e) => e.backend_error(),
151            Error::OnlineClientError(e) => e.backend_error(),
152            Error::RuntimeApiError(e) => e.backend_error(),
153            Error::EventsError(e) => e.backend_error(),
154            Error::BlockError(e) => e.backend_error(),
155            Error::ExtrinsicError(e) => e.backend_error(),
156            Error::ViewFunctionError(e) => e.backend_error(),
157            Error::TransactionProgressError(e) => e.backend_error(),
158            Error::TransactionEventsError(e) => e.backend_error(),
159            Error::TransactionFinalizedSuccessError(e) => e.backend_error(),
160            Error::StorageError(e) => e.backend_error(),
161            Error::OfflineClientAtBlockError(e) => e.backend_error(),
162            Error::OnlineClientAtBlockError(e) => e.backend_error(),
163            Error::ExtrinsicDecodeErrorAt(e) => e.backend_error(),
164            Error::ConstantError(e) => e.backend_error(),
165            Error::CustomValueError(e) => e.backend_error(),
166            Error::StorageKeyError(e) => e.backend_error(),
167            Error::StorageValueError(e) => e.backend_error(),
168            Error::TransactionStatusError(e) => e.backend_error(),
169            Error::ModuleErrorDetailsError(e) => e.backend_error(),
170            Error::ModuleErrorDecodeError(e) => e.backend_error(),
171            Error::DispatchErrorDecodeError(e) => e.backend_error(),
172            Error::CombinedBackendError(e) => e.backend_error(),
173            #[cfg(feature = "light-client")]
174            Error::OtherLightClientError(_) => None,
175            #[cfg(feature = "light-client")]
176            Error::OtherLightClientRpcError(_) => None,
177            // BackendError is always a BackendError:
178            Error::BackendError(e) => Some(e),
179            // Other errors come from different crates so can never contain a BackendError:
180            Error::OtherRpcClientError(_) => None,
181            Error::OtherCodecError(_) => None,
182            Error::Other(_) => None,
183        }
184    }
185}
186
187/// Errors constructing an offline client at a specific block number.
188#[allow(missing_docs)]
189#[derive(Debug, thiserror::Error)]
190#[non_exhaustive]
191pub enum OfflineClientAtBlockError {
192    #[error(
193        "Cannot construct OfflineClientAtBlock: spec version not found for block number {block_number}"
194    )]
195    SpecVersionNotFound {
196        /// The block number for which the spec version was not found.
197        block_number: u64,
198    },
199    #[error(
200        "Cannot construct OfflineClientAtBlock: metadata not found for spec version {spec_version}"
201    )]
202    MetadataNotFound {
203        /// The spec version for which the metadata was not found.
204        spec_version: u32,
205    },
206}
207
208impl OfflineClientAtBlockError {
209    fn backend_error(&self) -> Option<&BackendError> {
210        None
211    }
212}
213
214#[derive(Debug, thiserror::Error)]
215#[non_exhaustive]
216#[allow(missing_docs)]
217pub enum OnlineClientError {
218    #[error("Cannot construct OnlineClient: {0}")]
219    RpcError(#[from] subxt_rpcs::Error),
220    #[error("Could not construct the CombinedBackend: {0}")]
221    CannotBuildCombinedBackend(CombinedBackendError),
222    #[error("Cannot construct OnlineClient: Cannot fetch genesis hash: {0}")]
223    CannotGetGenesisHash(BackendError),
224}
225
226impl OnlineClientError {
227    fn backend_error(&self) -> Option<&BackendError> {
228        match self {
229            OnlineClientError::CannotGetGenesisHash(e) => Some(e),
230            _ => None,
231        }
232    }
233}
234
235/// Errors constructing streams of blocks.
236#[allow(missing_docs)]
237#[derive(Debug, thiserror::Error)]
238#[non_exhaustive]
239pub enum BlocksError {
240    #[error("Cannot construct block stream: cannot get the current block: {0}")]
241    CannotGetCurrentBlock(OnlineClientAtBlockError),
242    #[error("Cannot construct block stream: cannot get block header stream: {0}")]
243    CannotGetBlockHeaderStream(BackendError),
244    #[error("Error streaming blocks: cannot get the next block header: {0}")]
245    CannotGetBlockHeader(BackendError),
246}
247
248impl BlocksError {
249    fn backend_error(&self) -> Option<&BackendError> {
250        match self {
251            BlocksError::CannotGetCurrentBlock(e) => e.backend_error(),
252            BlocksError::CannotGetBlockHeaderStream(e) => Some(e),
253            BlocksError::CannotGetBlockHeader(e) => Some(e),
254        }
255    }
256}
257
258/// Errors constructing an online client at a specific block number.
259#[allow(missing_docs)]
260#[derive(Debug, thiserror::Error)]
261#[non_exhaustive]
262pub enum OnlineClientAtBlockError {
263    #[error("Cannot construct OnlineClientAtBlock: cannot get the current block: {reason}")]
264    CannotGetCurrentBlock {
265        /// The error we encountered.
266        reason: BackendError,
267    },
268    #[error(
269        "Cannot construct OnlineClientAtBlock: failed to get block hash from node for block {block_number}: {reason}"
270    )]
271    CannotGetBlockHash {
272        /// Block number we failed to get the hash for.
273        block_number: u64,
274        /// The error we encountered.
275        reason: BackendError,
276    },
277    #[error("Cannot construct OnlineClientAtBlock: block number {block_number} not found")]
278    BlockNotFound {
279        /// The block number for which a block was not found.
280        block_number: u64,
281    },
282    #[error(
283        "Cannot construct OnlineClientAtBlock: cannot get the block header for block {block_hash}: {reason}"
284    )]
285    CannotGetBlockHeader {
286        /// Block hash that we failed to fetch the header for.
287        block_hash: Hex,
288        /// The error we encountered.
289        reason: BackendError,
290    },
291    #[error(
292        "Cannot construct OnlineClientAtBlock: cannot find the block header for block {block_hash}"
293    )]
294    BlockHeaderNotFound {
295        /// Block hash that we failed to find the header for.
296        block_hash: Hex,
297    },
298    #[error(
299        "Cannot construct OnlineClientAtBlock: failed to obtain spec version for block {block_hash}: {reason}"
300    )]
301    CannotGetSpecVersion {
302        /// The block hash for which we failed to obtain the spec version.
303        block_hash: Hex,
304        /// The error we encountered.
305        reason: BackendError,
306    },
307    #[error(
308        "Cannot construct OnlineClientAtBlock: failed to decode spec version for block {block_hash}: {reason}"
309    )]
310    CannotDecodeSpecVersion {
311        /// The block hash for which we failed to decode the spec version.
312        block_hash: Hex,
313        /// The error we encountered.
314        reason: codec::Error,
315    },
316    #[error(
317        "Cannot construct OnlineClientAtBlock: failed to get metadata for block {block_hash}: {reason}"
318    )]
319    CannotGetMetadata {
320        /// The block hash for which we failed to get the metadata.
321        block_hash: Hex,
322        /// The error we encountered.
323        reason: String,
324    },
325    #[error(
326        "Cannot construct OnlineClientAtBlock: Metadata V{version} (required at block {block_hash} is not supported."
327    )]
328    UnsupportedMetadataVersion {
329        /// The block hash that requires the unsupported version.
330        block_hash: Hex,
331        /// The unsupported metadata version.
332        version: u32,
333    },
334    #[error(
335        "Cannot construct OnlineClientAtBlock: No legacy types were provided but we're trying to access a block that requires them."
336    )]
337    MissingLegacyTypes,
338    #[error(
339        "Cannot construct OnlineClientAtBlock: unable to convert legacy metadata (required at block {block_hash}): {reason}"
340    )]
341    CannotConvertLegacyMetadata {
342        /// The block hash that requires legacy types.
343        block_hash: Hex,
344        /// The metadata version.
345        metadata_version: u32,
346        /// Reason the conversion failed.
347        reason: subxt_metadata::LegacyFromError,
348    },
349    #[error(
350        "Cannot construct OnlineClientAtBlock: unable to convert modern metadata (required at block {block_hash}): {reason}"
351    )]
352    CannotConvertModernMetadata {
353        /// The block hash that requires legacy types.
354        block_hash: Hex,
355        /// The metadata version.
356        metadata_version: u32,
357        /// Reason the conversion failed.
358        reason: subxt_metadata::TryFromError,
359    },
360    #[error(
361        "Cannot construct OnlineClientAtBlock: cannot inject types from metadata: failure to parse a type found in the metadata: {parse_error}"
362    )]
363    CannotInjectMetadataTypes {
364        /// Error parsing a type found in the metadata.
365        parse_error: scale_info_legacy::lookup_name::ParseError,
366    },
367}
368
369impl OnlineClientAtBlockError {
370    fn backend_error(&self) -> Option<&BackendError> {
371        match self {
372            OnlineClientAtBlockError::CannotGetCurrentBlock { reason }
373            | OnlineClientAtBlockError::CannotGetBlockHash { reason, .. }
374            | OnlineClientAtBlockError::CannotGetBlockHeader { reason, .. }
375            | OnlineClientAtBlockError::CannotGetSpecVersion { reason, .. } => Some(reason),
376            _ => None,
377        }
378    }
379}
380
381#[derive(Debug, thiserror::Error)]
382#[non_exhaustive]
383#[allow(missing_docs)]
384pub enum BlockError {
385    #[error("Could not find the block with hash {block_hash}")]
386    BlockNotFound { block_hash: Hex },
387    #[error("Could not download the block header with hash {block_hash}: {reason}")]
388    CouldNotDownloadBlockHeader {
389        block_hash: Hex,
390        reason: BackendError,
391    },
392}
393
394impl BlockError {
395    fn backend_error(&self) -> Option<&BackendError> {
396        match self {
397            BlockError::CouldNotDownloadBlockHeader { reason, .. } => Some(reason),
398            _ => None,
399        }
400    }
401}
402
403#[derive(Debug, thiserror::Error)]
404#[non_exhaustive]
405#[allow(missing_docs)]
406pub enum BackendError {
407    #[error("Backend error: RPC error: {0}")]
408    Rpc(#[from] RpcError),
409    // This is for errors in `Backend` implementations which aren't any of the "pre-defined" set above:
410    #[error("Custom backend error: {0}")]
411    Other(Cow<'static, str>),
412}
413
414impl BackendError {
415    /// Checks whether the error was caused by a RPC re-connection.
416    pub fn is_disconnected_will_reconnect(&self) -> bool {
417        matches!(
418            self,
419            BackendError::Rpc(RpcError::ClientError(
420                subxt_rpcs::Error::DisconnectedWillReconnect(_)
421            ))
422        )
423    }
424
425    /// Checks whether the error was caused by a RPC request being rejected.
426    pub fn is_rpc_limit_reached(&self) -> bool {
427        matches!(self, BackendError::Rpc(RpcError::LimitReached))
428    }
429
430    /// Create a [`BackendError::Other`] given a message.
431    pub fn other(message: impl Into<Cow<'static, str>>) -> Self {
432        BackendError::Other(message.into())
433    }
434}
435
436impl From<subxt_rpcs::Error> for BackendError {
437    fn from(value: subxt_rpcs::Error) -> Self {
438        BackendError::Rpc(RpcError::ClientError(value))
439    }
440}
441
442#[derive(Debug, thiserror::Error)]
443#[non_exhaustive]
444#[allow(missing_docs)]
445pub enum CombinedBackendError {
446    #[error("Could not obtain the list of RPC methods to determine which backends can be used")]
447    CouldNotObtainRpcMethodList(subxt_rpcs::Error),
448}
449
450impl CombinedBackendError {
451    fn backend_error(&self) -> Option<&BackendError> {
452        None
453    }
454}
455
456/// An RPC error. Since we are generic over the RPC client that is used,
457/// the error is boxed and could be casted.
458#[derive(Debug, thiserror::Error)]
459#[non_exhaustive]
460pub enum RpcError {
461    /// Error related to the RPC client.
462    #[error("RPC error: {0}")]
463    ClientError(#[from] subxt_rpcs::Error),
464    /// This error signals that we got back a [`subxt_rpcs::methods::chain_head::MethodResponse::LimitReached`],
465    /// which is not technically an RPC error but is treated as an error in our own APIs.
466    #[error("RPC error: limit reached")]
467    LimitReached,
468    /// The RPC subscription was dropped.
469    #[error("RPC error: subscription dropped.")]
470    SubscriptionDropped,
471}
472
473#[derive(Debug, thiserror::Error)]
474#[non_exhaustive]
475#[allow(missing_docs)]
476pub enum AccountNonceError {
477    #[error("Could not retrieve account nonce: {0}")]
478    CouldNotRetrieve(BackendError),
479    #[error("Could not decode account nonce: {0}")]
480    CouldNotDecode(codec::Error),
481    #[error("Wrong number of account nonce bytes returned: {0} (expected 2, 4 or 8)")]
482    WrongNumberOfBytes(usize),
483}
484
485impl AccountNonceError {
486    fn backend_error(&self) -> Option<&BackendError> {
487        match self {
488            AccountNonceError::CouldNotRetrieve(e) => Some(e),
489            _ => None,
490        }
491    }
492}
493
494/// Error working with Runtime APIs
495#[non_exhaustive]
496#[derive(Debug, thiserror::Error)]
497#[allow(missing_docs)]
498pub enum RuntimeApiError {
499    #[error("The static Runtime API address used is not compatible with the live chain")]
500    IncompatibleCodegen,
501    #[error("Runtime API trait not found: {0}")]
502    TraitNotFound(String),
503    #[error("Runtime API method {method_name} not found in trait {trait_name}")]
504    MethodNotFound {
505        trait_name: String,
506        method_name: String,
507    },
508    #[error("Failed to encode Runtime API inputs: {0}")]
509    CouldNotEncodeInputs(frame_decode::runtime_apis::RuntimeApiInputsEncodeError),
510    #[error("Failed to decode Runtime API: {0}")]
511    CouldNotDecodeResponse(frame_decode::runtime_apis::RuntimeApiDecodeError<u32>),
512    #[error("Cannot call the Runtime API: {0}")]
513    CannotCallApi(BackendError),
514}
515
516impl RuntimeApiError {
517    fn backend_error(&self) -> Option<&BackendError> {
518        match self {
519            RuntimeApiError::CannotCallApi(e) => Some(e),
520            _ => None,
521        }
522    }
523}
524
525/// Error working with events.
526#[non_exhaustive]
527#[derive(Debug, thiserror::Error)]
528#[allow(missing_docs)]
529pub enum EventsError {
530    #[error("Can't decode event: can't decode phase: {0}")]
531    CannotDecodePhase(codec::Error),
532    #[error("Can't decode event: can't decode pallet index: {0}")]
533    CannotDecodePalletIndex(codec::Error),
534    #[error("Can't decode event: can't decode variant index: {0}")]
535    CannotDecodeVariantIndex(codec::Error),
536    #[error("Can't decode event: can't find pallet with index {0}")]
537    CannotFindPalletWithIndex(u8),
538    #[error(
539        "Can't decode event: can't find variant with index {variant_index} in pallet {pallet_name}"
540    )]
541    CannotFindVariantWithIndex {
542        pallet_name: String,
543        variant_index: u8,
544    },
545    #[error("Can't decode field {field_name:?} in event {pallet_name}.{event_name}: {reason}")]
546    CannotDecodeFieldInEvent {
547        pallet_name: String,
548        event_name: String,
549        field_name: String,
550        reason: scale_decode::visitor::DecodeError,
551    },
552    #[error("Can't decode event topics: {0}")]
553    CannotDecodeEventTopics(codec::Error),
554    #[error("Can't decode the fields of event {pallet_name}.{event_name}: {reason}")]
555    CannotDecodeEventFields {
556        pallet_name: String,
557        event_name: String,
558        reason: scale_decode::Error,
559    },
560    #[error("Can't decode event {pallet_name}.{event_name} to Event enum: {reason}")]
561    CannotDecodeEventEnum {
562        pallet_name: String,
563        event_name: String,
564        reason: scale_decode::Error,
565    },
566    #[error("Cannot fetch event bytes: {0}")]
567    CannotFetchEventBytes(BackendError),
568}
569
570impl EventsError {
571    fn backend_error(&self) -> Option<&BackendError> {
572        match self {
573            EventsError::CannotFetchEventBytes(e) => Some(e),
574            _ => None,
575        }
576    }
577}
578
579/// Error working with extrinsics.
580#[non_exhaustive]
581#[derive(Debug, thiserror::Error)]
582#[allow(missing_docs)]
583pub enum ExtrinsicError {
584    #[error("Failed to construct extrinsic: {0}")]
585    EncodeError(#[from] frame_decode::extrinsics::ExtrinsicEncodeError),
586    #[error("The extrinsic payload is not compatible with the live chain")]
587    IncompatibleCodegen,
588    #[error("Can't find extrinsic: pallet with name {0} not found")]
589    PalletNameNotFound(String),
590    #[error("Can't find extrinsic: call name {call_name} doesn't exist in pallet {pallet_name}")]
591    CallNameNotFound {
592        pallet_name: String,
593        call_name: String,
594    },
595    #[error("Failed to encode an extrinsic: the genesis hash was not provided")]
596    GenesisHashNotProvided,
597    #[error("Subxt does not support the extrinsic versions expected by the chain")]
598    UnsupportedVersion,
599    #[error("Cannot construct the required transaction extensions: {0}")]
600    Params(#[from] TransactionExtensionError),
601    #[error("Cannot decode transaction extension '{name}': {error}")]
602    CouldNotDecodeTransactionExtension {
603        /// The extension name.
604        name: String,
605        /// The decode error.
606        error: scale_decode::Error,
607    },
608    #[error("Failed to decode the fields of an extrinsic at index {extrinsic_index}: {error}")]
609    CannotDecodeFields {
610        /// Index of the extrinsic whose fields we could not decode
611        extrinsic_index: usize,
612        /// The decode error.
613        error: scale_decode::Error,
614    },
615    #[error("Failed to decode the extrinsic at index {extrinsic_index} to a root enum: {error}")]
616    CannotDecodeIntoRootExtrinsic {
617        /// Index of the extrinsic that we failed to decode
618        extrinsic_index: usize,
619        /// The decode error.
620        error: scale_decode::Error,
621    },
622    #[error(
623        "Cannot decode call data: expected at least 2 bytes (the pallet and call index) but got {0}"
624    )]
625    CallDataTooShort(usize),
626    #[error("Cannot decode call data: {0}")]
627    CannotDecodeCallData(frame_decode::extrinsics::ExtrinsicInfoError<'static>),
628    #[error("Cannot decode the call data arguments: {0}")]
629    CannotDecodeCallDataFields(scale_decode::Error),
630    #[error("Cannot decode call data: {0} bytes are left over after decoding it")]
631    LeftoverBytesDecodingCallData(usize),
632    #[error("Could not download block body to extract extrinsics from: {0}")]
633    CannotGetBlockBody(BackendError),
634    #[error("Block not found: {0}")]
635    BlockNotFound(Hex),
636    #[error("Error getting account nonce at block {block_hash}")]
637    AccountNonceError {
638        block_hash: Hex,
639        account_id: Hex,
640        reason: AccountNonceError,
641    },
642    #[error("Cannot submit extrinsic: {0}")]
643    ErrorSubmittingTransaction(BackendError),
644    #[error("A transaction status error was returned while submitting the extrinsic: {0}")]
645    TransactionStatusError(TransactionStatusError),
646    #[error(
647        "The transaction status stream encountered an error while submitting the extrinsic: {0}"
648    )]
649    TransactionStatusStreamError(BackendError),
650    #[error(
651        "The transaction status stream unexpectedly ended, so we don't know the status of the submitted extrinsic"
652    )]
653    UnexpectedEndOfTransactionStatusStream,
654    #[error("Cannot get fee info from Runtime API: {0}")]
655    CannotGetFeeInfo(BackendError),
656    #[error("Cannot decode fee info from Runtime API: {0}")]
657    CannotDecodeFeeInfo(codec::Error),
658    #[error("Cannot get validation info from Runtime API: {0}")]
659    CannotGetValidationInfo(BackendError),
660    #[error("Cannot decode ValidationResult bytes: {0}")]
661    CannotDecodeValidationResult(codec::Error),
662    #[error("ValidationResult bytes could not be decoded")]
663    UnexpectedValidationResultBytes(Vec<u8>),
664}
665
666impl ExtrinsicError {
667    fn backend_error(&self) -> Option<&BackendError> {
668        match self {
669            ExtrinsicError::CannotGetBlockBody(e)
670            | ExtrinsicError::ErrorSubmittingTransaction(e)
671            | ExtrinsicError::TransactionStatusStreamError(e)
672            | ExtrinsicError::CannotGetFeeInfo(e)
673            | ExtrinsicError::CannotGetValidationInfo(e) => Some(e),
674            ExtrinsicError::AccountNonceError { reason, .. } => reason.backend_error(),
675            _ => None,
676        }
677    }
678}
679
680#[derive(Debug, DeriveError)]
681#[non_exhaustive]
682#[allow(missing_docs)]
683pub enum CustomValueError {
684    #[error("The static custom value address used is not compatible with the live chain")]
685    IncompatibleCodegen,
686    #[error("The custom value '{0}' was not found")]
687    NotFound(String),
688    #[error("Failed to decode custom value: {0}")]
689    CouldNotDecodeCustomValue(frame_decode::custom_values::CustomValueDecodeError<u32>),
690}
691
692impl CustomValueError {
693    fn backend_error(&self) -> Option<&BackendError> {
694        None
695    }
696}
697
698/// Error working with View Functions.
699#[non_exhaustive]
700#[derive(Debug, thiserror::Error)]
701#[allow(missing_docs)]
702pub enum ViewFunctionError {
703    #[error("The static View Function address used is not compatible with the live chain")]
704    IncompatibleCodegen,
705    #[error("Can't find View Function: pallet {0} not found")]
706    PalletNotFound(String),
707    #[error("Can't find View Function {function_name} in pallet {pallet_name}")]
708    ViewFunctionNotFound {
709        pallet_name: String,
710        function_name: String,
711    },
712    #[error("Failed to encode View Function inputs: {0}")]
713    CouldNotEncodeInputs(frame_decode::view_functions::ViewFunctionInputsEncodeError),
714    #[error("Failed to decode View Function: {0}")]
715    CouldNotDecodeResponse(frame_decode::view_functions::ViewFunctionDecodeError<u32>),
716    #[error("Cannot call the View Function Runtime API: {0}")]
717    CannotCallApi(BackendError),
718}
719
720impl ViewFunctionError {
721    fn backend_error(&self) -> Option<&BackendError> {
722        match self {
723            ViewFunctionError::CannotCallApi(e) => Some(e),
724            _ => None,
725        }
726    }
727}
728
729/// Error during the transaction progress.
730#[non_exhaustive]
731#[derive(Debug, thiserror::Error)]
732#[allow(missing_docs)]
733pub enum TransactionProgressError {
734    #[error("Cannot get the next transaction progress update: {0}")]
735    CannotGetNextProgressUpdate(BackendError),
736    #[error("Error during transaction progress: {0}")]
737    TransactionStatusError(#[from] TransactionStatusError),
738    #[error(
739        "The transaction status stream unexpectedly ended, so we have no further transaction progress updates"
740    )]
741    UnexpectedEndOfTransactionStatusStream,
742}
743
744impl TransactionProgressError {
745    fn backend_error(&self) -> Option<&BackendError> {
746        match self {
747            TransactionProgressError::CannotGetNextProgressUpdate(e) => Some(e),
748            TransactionProgressError::TransactionStatusError(_) => None,
749            TransactionProgressError::UnexpectedEndOfTransactionStatusStream => None,
750        }
751    }
752}
753
754/// An error emitted as the result of a transaction progress update.
755#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
756#[non_exhaustive]
757#[allow(missing_docs)]
758pub enum TransactionStatusError {
759    /// An error happened on the node that the transaction was submitted to.
760    #[error("Error handling transaction: {0}")]
761    Error(String),
762    /// The transaction was deemed invalid.
763    #[error("The transaction is not valid: {0}")]
764    Invalid(String),
765    /// The transaction was dropped.
766    #[error("The transaction was dropped: {0}")]
767    Dropped(String),
768}
769
770impl TransactionStatusError {
771    fn backend_error(&self) -> Option<&BackendError> {
772        None
773    }
774}
775
776/// Error fetching events for a just-submitted transaction
777#[derive(Debug, thiserror::Error)]
778#[non_exhaustive]
779#[allow(missing_docs)]
780pub enum TransactionEventsError {
781    #[error(
782        "The block containing the submitted transaction ({block_hash}) could not be downloaded: {error}"
783    )]
784    CannotFetchBlockBody {
785        block_hash: Hex,
786        error: BackendError,
787    },
788    #[error(
789        "Cannot find the the submitted transaction (hash: {transaction_hash}) in the block (hash: {block_hash}) it is supposed to be in."
790    )]
791    CannotFindTransactionInBlock {
792        block_hash: Hex,
793        transaction_hash: Hex,
794    },
795    #[error("The block containing the submitted transaction ({block_hash}) could not be found")]
796    BlockNotFound { block_hash: Hex },
797    #[error(
798        "Could not decode event at index {event_index} for the submitted transaction at block {block_hash}: {error}"
799    )]
800    CannotDecodeEventInBlock {
801        event_index: usize,
802        block_hash: Hex,
803        error: EventsError,
804    },
805    #[error("Could not instantiate a client at the required block to fetch events: {0}")]
806    CannotInstantiateClientAtBlock(OnlineClientAtBlockError),
807    #[error("Could not fetch events for the submitted transaction: {error}")]
808    CannotFetchEventsForTransaction {
809        block_hash: Hex,
810        transaction_hash: Hex,
811        error: EventsError,
812    },
813    #[error("The transaction led to a DispatchError, but we failed to decode it: {error}")]
814    CannotDecodeDispatchError {
815        error: DispatchErrorDecodeError,
816        bytes: Vec<u8>,
817    },
818    #[error("The transaction failed with the following dispatch error: {0}")]
819    ExtrinsicFailed(#[from] DispatchError),
820}
821
822impl TransactionEventsError {
823    fn backend_error(&self) -> Option<&BackendError> {
824        match self {
825            TransactionEventsError::CannotFetchBlockBody { error, .. } => Some(error),
826            TransactionEventsError::CannotDecodeEventInBlock { error, .. }
827            | TransactionEventsError::CannotFetchEventsForTransaction { error, .. } => {
828                error.backend_error()
829            }
830            _ => None,
831        }
832    }
833}
834
835/// Error waiting for the transaction to be finalized and successful.
836#[derive(Debug, thiserror::Error)]
837#[non_exhaustive]
838#[allow(missing_docs, clippy::large_enum_variant)]
839pub enum TransactionFinalizedSuccessError {
840    #[error("Could not finalize the transaction: {0}")]
841    FinalizationError(#[from] TransactionProgressError),
842    #[error("The transaction did not succeed: {0}")]
843    SuccessError(#[from] TransactionEventsError),
844}
845
846impl TransactionFinalizedSuccessError {
847    fn backend_error(&self) -> Option<&BackendError> {
848        match self {
849            TransactionFinalizedSuccessError::FinalizationError(e) => e.backend_error(),
850            TransactionFinalizedSuccessError::SuccessError(e) => e.backend_error(),
851        }
852    }
853}
854
855/// Error decoding the [`DispatchError`]
856#[derive(Debug, thiserror::Error)]
857#[non_exhaustive]
858#[allow(missing_docs)]
859pub enum ModuleErrorDetailsError {
860    #[error(
861        "Could not get details for the DispatchError: could not find pallet index {pallet_index}"
862    )]
863    PalletNotFound { pallet_index: u8 },
864    #[error(
865        "Could not get details for the DispatchError: could not find error index {error_index} in pallet {pallet_name}"
866    )]
867    ErrorVariantNotFound {
868        pallet_name: String,
869        error_index: u8,
870    },
871}
872
873impl ModuleErrorDetailsError {
874    fn backend_error(&self) -> Option<&BackendError> {
875        None
876    }
877}
878
879/// Error decoding the [`ModuleError`]
880#[derive(Debug, thiserror::Error)]
881#[non_exhaustive]
882#[allow(missing_docs)]
883#[error("Could not decode the DispatchError::Module payload into the given type: {0}")]
884pub struct ModuleErrorDecodeError(scale_decode::Error);
885
886impl ModuleErrorDecodeError {
887    fn backend_error(&self) -> Option<&BackendError> {
888        None
889    }
890}
891
892/// Error decoding the [`DispatchError`]
893#[derive(Debug, thiserror::Error)]
894#[non_exhaustive]
895#[allow(missing_docs)]
896pub enum DispatchErrorDecodeError {
897    #[error(
898        "Could not decode the DispatchError: could not find the corresponding type ID in the metadata"
899    )]
900    DispatchErrorTypeIdNotFound,
901    #[error("Could not decode the DispatchError: {0}")]
902    CouldNotDecodeDispatchError(scale_decode::Error),
903    #[error("Could not decode the DispatchError::Module variant")]
904    CouldNotDecodeModuleError {
905        /// The bytes corresponding to the Module variant we were unable to decode:
906        bytes: Vec<u8>,
907    },
908}
909
910impl DispatchErrorDecodeError {
911    fn backend_error(&self) -> Option<&BackendError> {
912        None
913    }
914}
915
916/// Error working with storage.
917#[derive(Debug, thiserror::Error)]
918#[non_exhaustive]
919#[allow(missing_docs)]
920pub enum StorageError {
921    #[error("The static storage address used is not compatible with the live chain")]
922    IncompatibleCodegen,
923    #[error("Can't find storage value: pallet with name {0} not found")]
924    PalletNameNotFound(String),
925    #[error(
926        "Storage entry '{entry_name}' not found in pallet {pallet_name} in the live chain metadata"
927    )]
928    StorageEntryNotFound {
929        pallet_name: String,
930        entry_name: String,
931    },
932    #[error("Cannot obtain storage information from metadata: {0}")]
933    StorageInfoError(frame_decode::storage::StorageInfoError<'static>),
934    #[error("Cannot encode storage key: {0}")]
935    StorageKeyEncodeError(frame_decode::storage::StorageKeyEncodeError),
936    #[error("Cannot create a key to iterate over a plain entry")]
937    CannotIterPlainEntry {
938        pallet_name: String,
939        entry_name: String,
940    },
941    #[error(
942        "Wrong number of key parts provided to iterate a storage address. We expected at most {max_expected} key parts but got {got} key parts"
943    )]
944    WrongNumberOfKeyPartsProvidedForIterating { max_expected: usize, got: usize },
945    #[error(
946        "Wrong number of key parts provided to fetch a storage address. We expected {expected} key parts but got {got} key parts"
947    )]
948    WrongNumberOfKeyPartsProvidedForFetching { expected: usize, got: usize },
949    #[error(
950        "No storage value found at the given address, and no default value to fall back to using."
951    )]
952    NoValueFound,
953    #[error("Cannot fetch the storage value: {0}")]
954    CannotFetchValue(BackendError),
955    #[error("Cannot iterate storage values: {0}")]
956    CannotIterateValues(BackendError),
957    #[error("Encountered an error iterating over storage values: {0}")]
958    StreamFailure(BackendError),
959    #[error("Cannot decode the storage version for a given entry: {0}")]
960    CannotDecodeStorageVersion(codec::Error),
961}
962
963impl StorageError {
964    fn backend_error(&self) -> Option<&BackendError> {
965        match self {
966            StorageError::CannotFetchValue(e)
967            | StorageError::CannotIterateValues(e)
968            | StorageError::StreamFailure(e) => Some(e),
969            _ => None,
970        }
971    }
972}
973
974/// Something went wrong working with a constant.
975#[derive(Debug, DeriveError)]
976#[non_exhaustive]
977#[allow(missing_docs)]
978pub enum ConstantError {
979    #[error("The static constant address used is not compatible with the live chain")]
980    IncompatibleCodegen,
981    #[error("Can't find constant: pallet with name {0} not found")]
982    PalletNameNotFound(String),
983    #[error(
984        "Constant '{constant_name}' not found in pallet {pallet_name} in the live chain metadata"
985    )]
986    ConstantNameNotFound {
987        pallet_name: String,
988        constant_name: String,
989    },
990    #[error("Failed to decode constant: {0}")]
991    CouldNotDecodeConstant(frame_decode::constants::ConstantDecodeError<u32>),
992    #[error("Cannot obtain constant information from metadata: {0}")]
993    ConstantInfoError(frame_decode::constants::ConstantInfoError<'static>),
994}
995
996impl ConstantError {
997    fn backend_error(&self) -> Option<&BackendError> {
998        None
999    }
1000}
1001
1002#[derive(Debug, DeriveError)]
1003#[non_exhaustive]
1004#[allow(missing_docs)]
1005pub enum StorageKeyError {
1006    #[error("Can't decode the storage key: {error}")]
1007    StorageKeyDecodeError {
1008        bytes: Vec<u8>,
1009        error: frame_decode::storage::StorageKeyDecodeError<u32>,
1010    },
1011    #[error("Can't decode the values from the storage key: {0}")]
1012    CannotDecodeValuesInKey(frame_decode::storage::StorageKeyValueDecodeError),
1013    #[error(
1014        "Cannot decode storage key: there were leftover bytes, indicating that the decoding failed"
1015    )]
1016    LeftoverBytes { bytes: Vec<u8> },
1017    #[error("Can't decode a single value from the storage key part at index {index}: {error}")]
1018    CannotDecodeValueInKey {
1019        index: usize,
1020        error: scale_decode::Error,
1021    },
1022}
1023
1024impl StorageKeyError {
1025    fn backend_error(&self) -> Option<&BackendError> {
1026        None
1027    }
1028}
1029
1030#[derive(Debug, DeriveError)]
1031#[non_exhaustive]
1032#[allow(missing_docs)]
1033pub enum StorageValueError {
1034    #[error("Cannot decode storage value: {0}")]
1035    CannotDecode(frame_decode::storage::StorageValueDecodeError<u32>),
1036    #[error(
1037        "Cannot decode storage value: there were leftover bytes, indicating that the decoding failed"
1038    )]
1039    LeftoverBytes { bytes: Vec<u8> },
1040}
1041
1042impl StorageValueError {
1043    fn backend_error(&self) -> Option<&BackendError> {
1044        None
1045    }
1046}
1047
1048#[derive(Debug, thiserror::Error)]
1049#[non_exhaustive]
1050#[allow(missing_docs)]
1051#[error("Cannot decode extrinsic at index {extrinsic_index}: {error}")]
1052pub struct ExtrinsicDecodeErrorAt {
1053    pub extrinsic_index: usize,
1054    pub error: ExtrinsicDecodeErrorAtReason,
1055}
1056
1057impl ExtrinsicDecodeErrorAt {
1058    fn backend_error(&self) -> Option<&BackendError> {
1059        None
1060    }
1061}
1062
1063#[derive(Debug, thiserror::Error)]
1064#[non_exhaustive]
1065#[allow(missing_docs)]
1066pub enum ExtrinsicDecodeErrorAtReason {
1067    #[error("{0}")]
1068    DecodeError(frame_decode::extrinsics::ExtrinsicDecodeError),
1069    #[error("Leftover bytes")]
1070    LeftoverBytes(Vec<u8>),
1071}
1072
1073/// An error that can be emitted when trying to construct a [`crate::config::TransactionExtension`],
1074/// encode data from the instance, or match on signed extensions.
1075#[derive(Debug, DeriveError)]
1076#[non_exhaustive]
1077#[allow(missing_docs)]
1078pub enum TransactionExtensionError {
1079    #[error("Error constructing extrinsic parameters: {0}")]
1080    Custom(Box<dyn core::error::Error + Send + Sync + 'static>),
1081}
1082
1083impl TransactionExtensionError {
1084    /// Create a custom [`TransactionExtensionError`] from a string.
1085    pub fn custom<S: Into<String>>(error: S) -> Self {
1086        let error: String = error.into();
1087        let error: Box<dyn core::error::Error + Send + Sync + 'static> = Box::from(error);
1088        TransactionExtensionError::Custom(error)
1089    }
1090}
1091
1092impl From<core::convert::Infallible> for TransactionExtensionError {
1093    fn from(value: core::convert::Infallible) -> Self {
1094        match value {}
1095    }
1096}