use anyhow::Result;
use claude_agent_sdk::{ClaudeAgentOptions, ClaudeError, Message, query};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<()> {
println!("=== Error Handling Example ===\n");
println!("1. API Error Handling:");
match handle_api_errors().await {
Ok(_) => println!(" ✓ Query succeeded"),
Err(e) => println!(" ✗ Query failed: {}", e),
}
println!("\n2. Timeout Handling:");
match handle_timeout().await {
Ok(_) => println!(" ✓ Query completed in time"),
Err(e) => println!(" ✗ Query timed out: {}", e),
}
println!("\n3. Permission Error Handling:");
match handle_permission_denied().await {
Ok(_) => println!(" ✓ Permission granted"),
Err(e) => println!(" ✗ Permission denied: {}", e),
}
println!("\n4. Tool Error Handling:");
match handle_tool_errors().await {
Ok(messages) => println!(
" ✓ Tools executed successfully, {} messages",
messages.len()
),
Err(e) => println!(" ✗ Tool execution failed: {}", e),
}
println!("\n5. Custom Error Recovery:");
match custom_recovery_strategy().await {
Ok(result) => println!(" ✓ Recovered and got result: {}", result),
Err(e) => println!(" ✗ Recovery failed: {}", e),
}
println!("\n=== Error Handling Complete ===");
Ok(())
}
async fn handle_api_errors() -> Result<()> {
let options = ClaudeAgentOptions::builder().max_turns(3).build();
match query("What is 2 + 2?", Some(options)).await {
Ok(messages) => {
println!(" Got {} messages", messages.len());
Ok(())
},
Err(ClaudeError::Transport(e)) => {
eprintln!(" API Error: {}", e);
Err(anyhow::anyhow!("API Error: {}", e))
},
Err(e) => {
eprintln!(" Unexpected Error: {}", e);
Err(e.into())
},
}
}
async fn handle_timeout() -> Result<()> {
use std::time::Duration;
let timeout_duration = Duration::from_secs(30);
println!(
" Setting timeout to {} seconds",
timeout_duration.as_secs()
);
let _ = tokio::time::timeout(
timeout_duration,
query("Quick question: What's the capital of France?", None),
)
.await
.map_err(|_| anyhow::anyhow!("Query timed out after {:?}", timeout_duration))??;
Ok(())
}
async fn handle_permission_denied() -> Result<()> {
use claude_agent_sdk::PermissionMode;
let options = ClaudeAgentOptions::builder()
.permission_mode(PermissionMode::BypassPermissions)
.build();
let messages = query("Create a test file", Some(options)).await?;
println!(" Successfully executed without permission prompts");
println!(" Received {} messages", messages.len());
Ok(())
}
async fn handle_tool_errors() -> Result<Vec<Message>> {
let options = ClaudeAgentOptions::builder()
.allowed_tools(vec![
"Read".to_string(),
])
.build();
let messages = query("Try to read a file and then write to it", Some(options)).await?;
for msg in &messages {
if let Message::Assistant(assistant_msg) = msg {
for block in &assistant_msg.message.content {
if let claude_agent_sdk::ContentBlock::ToolUse(tool_use) = block {
if tool_use.name == "Bash" || tool_use.name == "Write" {
println!(" Tool {} was called", tool_use.name);
}
}
}
}
}
Ok(messages)
}
async fn custom_recovery_strategy() -> Result<String> {
let options = ClaudeAgentOptions::builder()
.model("claude-opus-4-5")
.fallback_model("claude-sonnet-4-5")
.max_turns(2)
.build();
let messages = query("What is Rust?", Some(options)).await?;
for msg in &messages {
if let Message::Assistant(assistant_msg) = msg {
for block in &assistant_msg.message.content {
if let claude_agent_sdk::ContentBlock::Text(text) = block {
if !text.text.is_empty() {
return Ok(text.text.clone());
}
}
}
}
}
Err(anyhow::anyhow!("No valid response received"))
}
#[allow(dead_code)]
async fn retry_with_backoff() -> Result<()> {
use std::time::Duration;
let max_retries = 3;
let mut attempt = 0;
loop {
attempt += 1;
match query("Test query", None).await {
Ok(_) => {
println!(" Success on attempt {}", attempt);
return Ok(());
},
Err(_e) if attempt < max_retries => {
let backoff = Duration::from_millis(100 * 2_u64.pow(attempt as u32));
println!(" Attempt {} failed, retrying in {:?}", attempt, backoff);
tokio::time::sleep(backoff).await;
},
Err(e) => {
println!(" All {} attempts failed", max_retries);
return Err(e.into());
},
}
}
}
#[derive(Debug)]
#[allow(dead_code)]
enum AppError {
QueryFailed(ClaudeError),
InvalidResponse(String),
Timeout(Duration),
}
impl std::fmt::Display for AppError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AppError::QueryFailed(e) => write!(f, "Query failed: {}", e),
AppError::InvalidResponse(msg) => write!(f, "Invalid response: {}", msg),
AppError::Timeout(d) => write!(f, "Operation timed out after {:?}", d),
}
}
}
impl std::error::Error for AppError {}
#[allow(dead_code)]
async fn structured_error_handling() -> Result<(), AppError> {
query("Test", None).await.map_err(AppError::QueryFailed)?;
Ok(())
}