Error Handling
The Tenzro Platform Rust SDK uses a comprehensive error type system for handling all failure cases with proper error propagation using the ? operator.
Error Types
use tenzro_platform::error::{
Error,
ApiError,
AuthenticationError,
AuthorizationError,
ValidationError,
NotFoundError,
RateLimitError,
InternalError,
NetworkError,
};Error Enum
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Authentication failed: {0}")]
Authentication(AuthenticationError),
#[error("Authorization failed: {0}")]
Authorization(AuthorizationError),
#[error("Validation error: {0}")]
Validation(ValidationError),
#[error("Resource not found: {0}")]
NotFound(NotFoundError),
#[error("Rate limit exceeded: {0}")]
RateLimit(RateLimitError),
#[error("Internal server error: {0}")]
Internal(InternalError),
#[error("Network error: {0}")]
Network(NetworkError),
#[error("Configuration error: {0}")]
Config(String),
}Basic Error Handling
use tenzro_platform::error::Error;
async fn create_wallet(platform: &TenzroPlatform) -> Result<(), Error> {
let wallet = platform.wallet().create(CreateWalletRequest {
name: "Treasury".to_string(),
wallet_type: WalletType::MultiSig,
chain: Chain::Ethereum,
..Default::default()
}).await?; // Propagate error with ?
println!("Created: {}", wallet.address);
Ok(())
}Pattern Matching
use tenzro_platform::error::{Error, ValidationError, RateLimitError};
match platform.wallet().create(request).await {
Ok(wallet) => {
println!("Wallet created: {}", wallet.address);
}
Err(Error::Authentication(e)) => {
eprintln!("Invalid API key: {}", e.message);
// Handle re-authentication
}
Err(Error::RateLimit(e)) => {
eprintln!("Rate limited, retry after {} seconds", e.retry_after);
tokio::time::sleep(Duration::from_secs(e.retry_after)).await;
// Retry the request
}
Err(Error::Validation(e)) => {
eprintln!("Validation errors:");
for field_error in &e.errors {
eprintln!(" {}: {}", field_error.field, field_error.message);
}
}
Err(Error::NotFound(e)) => {
eprintln!("Resource not found: {}", e.resource_id);
}
Err(e) => {
eprintln!("Unexpected error: {}", e);
}
}Error Properties
// All API errors have these properties
pub struct ApiError {
pub message: String,
pub code: String,
pub status: u16,
pub request_id: String,
}
// Validation errors include field-level details
pub struct ValidationError {
pub message: String,
pub code: String,
pub status: u16,
pub request_id: String,
pub errors: Vec<FieldError>,
}
pub struct FieldError {
pub field: String,
pub message: String,
pub code: String,
}
// Rate limit errors include retry information
pub struct RateLimitError {
pub message: String,
pub code: String,
pub status: u16,
pub request_id: String,
pub retry_after: u64, // Seconds until rate limit resets
}Result Type Alias
use tenzro_platform::Result;
// Equivalent to Result<T, tenzro_platform::Error>
async fn list_wallets(platform: &TenzroPlatform) -> Result<Vec<Wallet>> {
platform.wallet().list().send().await
}Converting to anyhow
use anyhow::{Context, Result};
async fn process_wallets() -> Result<()> {
let platform = TenzroPlatform::from_env()
.context("Failed to create platform client")?;
let wallets = platform.wallet().list().send().await
.context("Failed to list wallets")?;
for wallet in wallets {
println!("{}: {}", wallet.name, wallet.address);
}
Ok(())
}Retry with Backoff
use tenzro_platform::error::{Error, RateLimitError};
use tokio::time::{sleep, Duration};
async fn with_retry<T, F, Fut>(
mut f: F,
max_retries: u32,
) -> Result<T, Error>
where
F: FnMut() -> Fut,
Fut: std::future::Future<Output = Result<T, Error>>,
{
let mut attempts = 0;
loop {
match f().await {
Ok(result) => return Ok(result),
Err(Error::RateLimit(e)) if attempts < max_retries => {
let delay = Duration::from_secs(e.retry_after);
tracing::warn!("Rate limited, retrying in {:?}", delay);
sleep(delay).await;
attempts += 1;
}
Err(Error::Network(_)) if attempts < max_retries => {
let delay = Duration::from_millis(100 * 2_u64.pow(attempts));
tracing::warn!("Network error, retrying in {:?}", delay);
sleep(delay).await;
attempts += 1;
}
Err(e) => return Err(e),
}
}
}
// Usage
let wallet = with_retry(
|| platform.wallet().create(request.clone()),
3,
).await?;Network Error Handling
use tenzro_platform::error::{Error, NetworkError};
match platform.wallet().list().send().await {
Ok(wallets) => { /* ... */ }
Err(Error::Network(e)) => {
match e.kind {
NetworkErrorKind::Timeout => {
eprintln!("Request timed out");
}
NetworkErrorKind::ConnectionRefused => {
eprintln!("Could not connect to server");
}
NetworkErrorKind::DnsResolution => {
eprintln!("DNS resolution failed");
}
_ => {
eprintln!("Network error: {}", e);
}
}
}
Err(e) => {
eprintln!("Other error: {}", e);
}
}Custom Error Context
use tenzro_platform::error::Error;
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("Wallet operation failed: {0}")]
Wallet(#[from] Error),
#[error("Configuration error: {0}")]
Config(String),
#[error("Database error: {0}")]
Database(#[from] sqlx::Error),
}
async fn create_treasury_wallet(
platform: &TenzroPlatform,
db: &Database,
) -> Result<WalletId, AppError> {
let wallet = platform.wallet().create(CreateWalletRequest {
name: "Treasury".to_string(),
wallet_type: WalletType::MultiSig,
chain: Chain::Ethereum,
..Default::default()
}).await?; // Automatically converts to AppError::Wallet
db.save_wallet(&wallet).await?; // Converts to AppError::Database
Ok(wallet.id)
}Logging Errors
use tracing::{error, warn, info};
async fn process_transfer(platform: &TenzroPlatform, request: TransferRequest) {
match platform.token().transfer(request).await {
Ok(transfer) => {
info!(tx_hash = %transfer.tx_hash, "Transfer successful");
}
Err(Error::RateLimit(e)) => {
warn!(
retry_after = e.retry_after,
request_id = %e.request_id,
"Rate limited"
);
}
Err(e) => {
error!(
error = %e,
"Transfer failed"
);
}
}
}