Error Handling
a2a-rust uses a layered error model: protocol-level errors (A2aError), client errors (ClientError), and server errors (ServerError).
A2aError (Protocol Level)
Protocol errors defined by the A2A spec:
#![allow(unused)] fn main() { use a2a_protocol_sdk::types::error::{A2aError, ErrorCode}; // Common error codes ErrorCode::TaskNotFound // Task doesn't exist ErrorCode::TaskNotCancelable // Agent doesn't support cancellation ErrorCode::InvalidParams // Bad request parameters ErrorCode::MethodNotFound // Unknown method ErrorCode::InternalError // Server-side failure ErrorCode::UnsupportedOperation // Not implemented }
Handling Protocol Errors
#![allow(unused)] fn main() { match client.get_task(params).await { Ok(task) => println!("Got task: {}", task.id), Err(e) => { // Check the error type eprintln!("Error: {e}"); } } }
Client Errors
The client wraps transport and protocol errors:
#![allow(unused)] fn main() { match client.send_message(params).await { Ok(response) => { /* handle response */ } Err(e) => { // Transport errors (network, timeout, etc.) // Protocol errors (task not found, invalid params, etc.) // Parse errors (malformed response) eprintln!("Client error: {e}"); } } }
Timeout Errors
#![allow(unused)] fn main() { // Per-request timeout let client = ClientBuilder::new(url) .with_timeout(Duration::from_secs(5)) .build()?; // This will error if the agent takes longer than 5 seconds match client.send_message(params).await { Ok(response) => { /* success */ } Err(e) => { // Could be a timeout error eprintln!("Failed (possibly timeout): {e}"); } } }
Connection Errors
#![allow(unused)] fn main() { // Connection timeout let client = ClientBuilder::new(url) .with_connection_timeout(Duration::from_secs(2)) .build()?; }
Automatic Retries
Use RetryPolicy to automatically retry transient errors:
#![allow(unused)] fn main() { use a2a_protocol_client::RetryPolicy; let client = ClientBuilder::new(url) .with_retry_policy(RetryPolicy::default()) .build()?; }
You can check if an error is retryable programmatically:
#![allow(unused)] fn main() { match client.send_message(params).await { Err(e) if e.is_retryable() => println!("Transient error: {e}"), Err(e) => println!("Permanent error: {e}"), Ok(resp) => { /* ... */ } } }
Retryable errors include: Http, HttpClient, Timeout, and UnexpectedStatus with codes 429, 502, 503, or 504.
Server Errors
When building an agent, the ServerError type covers handler-level failures:
#![allow(unused)] fn main() { use a2a_protocol_sdk::server::ServerError; // Server errors are returned by RequestHandlerBuilder::build() // and by store/executor operations }
Best Practices
Don't Panic
a2a-rust never panics in library code. All fallible operations return Result. Follow the same pattern in your executors:
#![allow(unused)] fn main() { // Good: return an error return Err(A2aError::internal("processing failed".into())); // Bad: panic panic!("processing failed"); }
Executor Error Handling
In your AgentExecutor, catch errors and report them as status updates:
#![allow(unused)] fn main() { Box::pin(async move { queue.write(/* Working */).await?; match risky_operation().await { Ok(result) => { queue.write(/* ArtifactUpdate */).await?; queue.write(/* Completed */).await?; } Err(e) => { // Report failure through the protocol queue.write(StreamResponse::StatusUpdate(TaskStatusUpdateEvent { task_id: ctx.task_id.clone(), context_id: ContextId::new(ctx.context_id.clone()), status: TaskStatus { state: TaskState::Failed, message: Some(Message { id: MessageId::new(uuid::Uuid::new_v4().to_string()), role: MessageRole::Agent, parts: vec![Part::text(&e.to_string())], task_id: None, context_id: None, reference_task_ids: None, extensions: None, metadata: None, }), timestamp: None, }, metadata: None, })).await?; } } Ok(()) }) }
Stream Error Recovery
For streaming, handle errors per-event:
#![allow(unused)] fn main() { while let Some(event) = stream.next().await { match event { Ok(ev) => { /* process event */ } Err(e) => { eprintln!("Stream error: {e}"); // Decide: retry via resubscribe, or give up break; } } } }
Next Steps
- Testing Your Agent — Testing error scenarios
- Pitfalls & Lessons Learned — Common mistakes