Skip to main content

agent_client_protocol/concepts/
error_handling.rs

1//! Error handling patterns in agent-client-protocol.
2//!
3//! This chapter explains how errors work in agent-client-protocol callbacks and the difference
4//! between *protocol errors* (sent to the peer) and *connection errors* (which
5//! shut down the connection).
6//!
7//! # Callback Return Types
8//!
9//! Almost all agent-client-protocol callbacks return `Result<_, crate::Error>`. What happens
10//! when you return an `Err` depends on the context:
11//!
12//! **Returning `Err` from a callback shuts down the connection.**
13//!
14//! This is appropriate for truly unrecoverable situations—internal bugs,
15//! resource exhaustion, or when you want to terminate the connection.
16//! But most of the time, you want to send an error *to the peer* while
17//! keeping the connection alive.
18//!
19//! # Sending Protocol Errors
20//!
21//! To send an error response to a request (without closing the connection),
22//! use the request context's `respond` method:
23//!
24//! ```
25//! # use agent_client_protocol::{Client, Agent, ConnectTo};
26//! # use agent_client_protocol_test::{ValidateRequest, ValidateResponse};
27//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
28//! Client.builder()
29//!     .on_receive_request(async |request: ValidateRequest, responder, _cx| {
30//!         if request.data.is_empty() {
31//!             // Send error to peer, keep connection alive
32//!             responder.respond_with_error(agent_client_protocol::Error::invalid_params())?;
33//!             return Ok(());
34//!         }
35//!
36//!         // Process valid request...
37//!         responder.respond(ValidateResponse { is_valid: true, error: None })?;
38//!         Ok(())
39//!     }, agent_client_protocol::on_receive_request!())
40//! #   .connect_with(transport, async |_| Ok(())).await?;
41//! # Ok(())
42//! # }
43//! ```
44//!
45//! For sending error notifications (one-way error messages), use
46//! [`send_error_notification`][crate::ConnectionTo::send_error_notification]:
47//!
48//! ```
49//! # use agent_client_protocol::{Client, Agent, ConnectTo};
50//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
51//! # Client.builder().connect_with(transport, async |cx| {
52//! cx.send_error_notification(agent_client_protocol::Error::internal_error()
53//!     .data("Something went wrong"))?;
54//! # Ok(())
55//! # }).await?;
56//! # Ok(())
57//! # }
58//! ```
59//!
60//! # The `into_internal_error` Helper
61//!
62//! When working with external libraries that return their own error types,
63//! you need to convert them to `agent_client_protocol::Error`. The
64//! [`Error::into_internal_error`][crate::Error::into_internal_error] method
65//! provides a convenient way to do this:
66//!
67//! ```
68//! use agent_client_protocol::Error;
69//!
70//! # fn example() -> Result<(), agent_client_protocol::Error> {
71//! # let data = "hello";
72//! # let path = "/tmp/test.txt";
73//! // Convert any error type to agent_client_protocol::Error
74//! let value = serde_json::to_value(&data)
75//!     .map_err(Error::into_internal_error)?;
76//!
77//! // Or with a file operation
78//! let contents = std::fs::read_to_string(path)
79//!     .map_err(Error::into_internal_error);
80//! # Ok(())
81//! # }
82//! ```
83//!
84//! This wraps the original error's message in an internal error, which is
85//! appropriate for unexpected failures. For expected error conditions that
86//! should be communicated to the peer, create specific error types instead.
87//!
88//! # Error Types
89//!
90//! The [`Error`][crate::Error] type provides factory methods for common
91//! JSON-RPC error codes:
92//!
93//! - [`Error::parse_error()`][crate::Error::parse_error] - Invalid JSON
94//! - [`Error::invalid_request()`][crate::Error::invalid_request] - Malformed request
95//! - [`Error::method_not_found()`][crate::Error::method_not_found] - Unknown method
96//! - [`Error::invalid_params()`][crate::Error::invalid_params] - Bad parameters
97//! - [`Error::internal_error()`][crate::Error::internal_error] - Server error
98//!
99//! You can add context with `.data()`:
100//!
101//! ```
102//! let error = agent_client_protocol::Error::invalid_params()
103//!     .data(serde_json::json!({
104//!         "field": "timeout",
105//!         "reason": "must be positive"
106//!     }));
107//! ```
108//!
109//! # Summary
110//!
111//! | Situation | What to do |
112//! |-----------|------------|
113//! | Send error response to request | `responder.respond(Err(error))` then `Ok(())` |
114//! | Send error notification | `cx.send_error_notification(error)` then `Ok(())` |
115//! | Shut down connection | Return `Err(error)` from callback |
116//! | Convert external error | `.map_err(Error::into_internal_error)?` |