hiero_sdk/error.rs
1// SPDX-License-Identifier: Apache-2.0
2
3use std::error::Error as StdError;
4use std::result::Result as StdResult;
5
6use crate::entity_id::Checksum;
7use crate::{
8 AccountId,
9 Hbar,
10 Status,
11 TransactionId,
12};
13
14/// `Result<T, Error>`
15pub type Result<T> = StdResult<T, Error>;
16
17pub(crate) type BoxStdError = Box<dyn StdError + Send + Sync + 'static>;
18
19/// Represents any possible error from a fallible function in the Hiero SDK.
20#[derive(Debug, thiserror::Error)]
21#[non_exhaustive]
22pub enum Error {
23 /// Request timed out.
24 #[error("failed to complete request within the maximum time allowed; most recent attempt failed with: {0}")]
25 TimedOut(#[source] Box<Error>),
26
27 /// GRPC status code was an error.
28 #[error("grpc: {0:?}")]
29 GrpcStatus(#[from] tonic::Status),
30
31 /// Failed to parse an SDK type from a protobuf response.
32 #[error("failed to create a SDK type from a protobuf response: {0}")]
33 FromProtobuf(#[source] BoxStdError),
34
35 // todo: bikeshed this.
36 /// Freeze failed due to there being no explicitly set node account IDs and no client being provided to generate them.
37 #[error("freeze failed due to node account IDs being unset")]
38 FreezeUnsetNodeAccountIds,
39
40 /// A transaction failed pre-check.
41 ///
42 /// The transaction had the ID `transaction_id`.
43 ///
44 /// Caused by `status` being an error.
45 #[error("transaction `{transaction_id}` failed pre-check with status `{status:?}`")]
46 TransactionPreCheckStatus {
47 /// The status that caused the [`Transaction`](crate::Transaction) to fail pre-check.
48 status: Status,
49
50 /// The `TransactionId` of the failed [`Transaction`](crate::Transaction) .
51 transaction_id: Box<TransactionId>,
52
53 /// The estimated transaction fee, if the status was [`InsufficientTxFee`].
54 cost: Option<Hbar>,
55 },
56
57 /// A [`Query`](crate::Query) for `transaction_id` failed pre-check.
58 ///
59 /// Caused by `status` being an error.
60 #[error("query for transaction `{transaction_id}` failed pre-check with status `{status:?}`")]
61 QueryPreCheckStatus {
62 /// The `Status` that caused the [`Query`](crate::Query) to fail pre-check.
63 status: Status,
64 /// The associated transaction's ID.
65 transaction_id: Box<TransactionId>,
66 },
67
68 /// A [`Query`](crate::Query) failed pre-check.
69 ///
70 /// The query had an associated `PaymentTransaction` with ID `transaction_id`.
71 ///
72 /// Caused by `status` being an error.
73 #[error(
74 "query with payment transaction `{transaction_id}` failed pre-check with status `{status:?}`"
75 )]
76 QueryPaymentPreCheckStatus {
77 /// The `Status` that caused the [`Query`](crate::Query) to fail pre-check.
78 status: Status,
79 /// The associated `PaymentTransaction`'s `TransactionId`.
80 transaction_id: Box<TransactionId>,
81 },
82
83 /// A [`Query`](crate::Query) failed pre-check.
84 ///
85 /// The query had no `PaymentTransaction`.
86 ///
87 /// Caused by `status` being an error.
88 #[error("query with no payment transaction failed pre-check with status `{status:?}`")]
89 QueryNoPaymentPreCheckStatus {
90 /// The `Status` that caused the [`Query`](crate::Query) to fail pre-check.
91 status: Status,
92 },
93
94 /// Failed to parse a basic type from string
95 /// (ex. [`AccountId`](crate::AccountId), [`ContractId`](crate::ContractId), [`TransactionId`](crate::TransactionId), etc.).
96 #[error("failed to parse: {0}")]
97 BasicParse(#[source] BoxStdError),
98
99 /// An entity ID had an invalid checksum
100 #[error("entity ID {shard}.{realm}.{num}-{present_checksum} was incorrect")]
101 BadEntityId {
102 /// The shard number
103 shard: u64,
104 /// The realm number
105 realm: u64,
106 /// The entity number
107 num: u64,
108 /// The (invalid) checksum that was present on the entity ID
109 present_checksum: Checksum,
110 /// The checksum that SHOULD HAVE BEEN on the entity ID
111 expected_checksum: Checksum,
112 },
113
114 /// An entity ID cannot be converted to a string with a checksum, because it is in an alternate form,
115 /// such as an `alias` or `evm_address`
116 #[error("an entity ID with an `alias` or `evm_address` cannot have a checksum")]
117 CannotCreateChecksum,
118
119 /// Failed to parse a [`PublicKey`](crate::PublicKey) or [`PrivateKey`](crate::PrivateKey).
120 #[error("failed to parse a key: {0}")]
121 KeyParse(#[source] BoxStdError),
122
123 /// Failed to derive a [`PrivateKey`](crate::PrivateKey) from another `PrivateKey`.
124 ///
125 /// Examples of when this can happen (non-exhaustive):
126 /// - [`PrivateKey::derive`](fn@crate::PrivateKey::derive) when the `PrivateKey` doesn't have a chain code.
127 /// - [`PrivateKey::derive`](fn@crate::PrivateKey::derive)
128 /// or [`PrivateKey::legacy_derive`](fn@crate::PrivateKey::legacy_derive) on an `Ecsda` key.
129 #[error("Failed to derive a key: {0}")]
130 KeyDerive(#[source] BoxStdError),
131
132 /// Failed to parse a [`Mnemonic`](crate::Mnemonic) due to the given `reason`.
133 ///
134 /// the `Mnemonic` is provided because invalid `Mnemonics`
135 /// can technically still provide valid [`PrivateKeys`](crate::PrivateKey).
136 #[cfg(feature = "mnemonic")]
137 #[error("failed to parse a mnemonic: {reason}")]
138 MnemonicParse {
139 /// This error's source.
140 #[source]
141 reason: MnemonicParseError,
142 /// The `Mnemonic` in question.
143 mnemonic: crate::Mnemonic,
144 },
145
146 /// An error occurred while attempting to convert a [`Mnemonic`](crate::Mnemonic) to a [`PrivateKey`](crate::PrivateKey)
147 #[cfg(feature = "mnemonic")]
148 #[error("failed to convert a mnemonic to entropy: {0}")]
149 MnemonicEntropy(#[from] MnemonicEntropyError),
150
151 /// The [`Client`](crate::Client) had no payer account (operator)
152 /// and the attempted request had no explicit [`TransactionId`].
153 #[error("client must be configured with a payer account or requests must be given an explicit transaction id")]
154 NoPayerAccountOrTransactionId,
155
156 /// Cost of a [`Query`](crate::Query) is more expensive than `max_query_payment`.
157 ///
158 /// The actual cost of the `Query` is `query_cost`.
159 #[error("cost of {query_cost} without explicit payment is greater than the maximum allowed payment of {max_query_payment}")]
160 MaxQueryPaymentExceeded {
161 /// the configured maximum query payment.
162 max_query_payment: Hbar,
163
164 /// How much the query actually cost.
165 query_cost: Hbar,
166 },
167
168 /// The associated node account was not found in the network.
169 #[error("node account `{0}` was not found in the configured network")]
170 NodeAccountUnknown(Box<AccountId>),
171
172 /// Received an unrecognized status code from the Hiero Network.
173 ///
174 /// This can happen when the SDK is outdated, try updating your SDK.
175 #[error("received unrecognized status code: {0}, try updating your SDK")]
176 ResponseStatusUnrecognized(i32),
177
178 // fixme(sr): Citation needed (unsure if this is accurate).
179 /// Getting the receipt for `transaction_id` failed with `status`.
180 #[error("receipt for transaction `{transaction_id:?}` failed with status `{status:?}`")]
181 ReceiptStatus {
182 /// The Error's status code.
183 status: Status,
184 /// The [`Transaction`](crate::Transaction)'s ID.
185 transaction_id: Option<Box<TransactionId>>,
186 },
187
188 /// Failed to verify a signature.
189 #[error("failed to verify a signature: {0}")]
190 SignatureVerify(#[source] BoxStdError),
191}
192
193impl Error {
194 pub(crate) fn from_protobuf<E: Into<BoxStdError>>(error: E) -> Self {
195 Self::FromProtobuf(error.into())
196 }
197
198 pub(crate) fn key_parse<E: Into<BoxStdError>>(error: E) -> Self {
199 Self::KeyParse(error.into())
200 }
201
202 pub(crate) fn key_derive<E: Into<BoxStdError>>(error: E) -> Self {
203 Self::KeyDerive(error.into())
204 }
205
206 pub(crate) fn basic_parse<E: Into<BoxStdError>>(error: E) -> Self {
207 Self::BasicParse(error.into())
208 }
209
210 pub(crate) fn signature_verify(error: impl Into<BoxStdError>) -> Self {
211 Self::SignatureVerify(error.into())
212 }
213}
214
215/// Failed to parse a mnemonic.
216#[cfg(feature = "mnemonic")]
217#[derive(Debug, thiserror::Error)]
218#[non_exhaustive]
219pub enum MnemonicParseError {
220 /// The [`Mnemonic`](crate::Mnemonic) contains an unexpected length.
221 #[error("bad length: expected `12` or `24` words, found `{0}`")]
222 BadLength(usize),
223
224 /// The [`Mnemonic`](crate::Mnemonic) contains words that aren't in the wordlist.
225 #[error("unknown words at indecies: `{0:?}`")]
226 UnknownWords(Vec<usize>),
227
228 /// The [`Mnemonic`](crate::Mnemonic) has an invalid checksum.
229 #[error("checksum mismatch: expected `{expected:02x}`, found `{actual:02x}`")]
230 ChecksumMismatch {
231 /// The checksum that was expected.
232 expected: u8,
233 /// The checksum that was actually found.
234 actual: u8,
235 },
236}
237
238/// Failed to convert a [`Mnemonic`](crate::Mnemonic) to a [`PrivateKey`](crate::PrivateKey)
239// todo: find a better name before release.
240#[cfg(feature = "mnemonic")]
241#[derive(Debug, thiserror::Error)]
242#[non_exhaustive]
243pub enum MnemonicEntropyError {
244 /// Encountered a [`Mnemonic`](crate::Mnemonic) of unexpected length.
245 #[error("bad length: expected `{expected}` words, found {actual} words")]
246 BadLength {
247 /// The number of words that were expected (12, 22, or 24)
248 expected: usize,
249 /// The number of words that were actually found.
250 actual: usize,
251 },
252
253 /// The [`Mnemonic`](crate::Mnemonic) has an invalid checksum.
254 #[error("checksum mismatch: expected `{expected:02x}`, found `{actual:02x}`")]
255 ChecksumMismatch {
256 /// The checksum that was expected.
257 expected: u8,
258 /// The checksum that was actually found.
259 actual: u8,
260 },
261
262 /// Used a passphrase with a legacy [`Mnemonic`](crate::Mnemonic).
263 #[error("used a passphrase with a legacy mnemonic")]
264 LegacyWithPassphrase,
265}