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"
            );
        }
    }
}