Expand description
Error handling patterns in agent-client-protocol.
This chapter explains how errors work in agent-client-protocol callbacks and the difference between protocol errors (sent to the peer) and connection errors (which shut down the connection).
§Callback Return Types
Almost all agent-client-protocol callbacks return Result<_, crate::Error>.
What happens when you return an Err depends on the context. An incoming
request-handler error is sent to the peer as an Error Response, while an
incoming notification-handler error is logged without a reply. Errors from
connection-lifecycle callbacks can shut down the connection.
§Sending Protocol Errors
To choose an error response explicitly while handling a request, use the
request context’s respond method:
Client.v1()
.on_receive_request(async |request: ValidateRequest, responder, _cx| {
if request.data.is_empty() {
// Send error to peer, keep connection alive
responder.respond_with_error(agent_client_protocol::Error::invalid_params())?;
return Ok(());
}
// Process valid request...
responder.respond(ValidateResponse { is_valid: true, error: None })?;
Ok(())
}, agent_client_protocol::on_receive_request!())JSON-RPC notifications are one-way and cannot receive success or error replies. The SDK logs notification parse and handler errors without answering them. If an application needs to report a one-way failure, define a notification method for that purpose.
Malformed JSON and invalid request envelopes are handled at the SDK’s transport boundary. Applications do not need to construct uncorrelated Error Responses.
§Dropped Responders
Every request gets a response. If a Responder is
dropped without responding (and was not moved somewhere that responds,
such as SentRequest::forward_response_to),
the SDK answers the request with -32603 (Internal error), with data naming
the method, or with -32800 (Request cancelled) if the peer had cancelled
the request with $/cancel_request. If the handler that dropped the
responder returns an error, that error is sent instead. Nothing is sent if
a response was already sent or the connection is shutting down.
The automatic error is an ordinary error response: a dropped initialize
responder fails initialization, so the client can retry.
§The into_internal_error Helper
When working with external libraries that return their own error types,
you need to convert them to agent_client_protocol::Error. The
Error::into_internal_error method
provides a convenient way to do this:
use agent_client_protocol::Error;
// Convert any error type to agent_client_protocol::Error
let value = serde_json::to_value(&data)
.map_err(Error::into_internal_error)?;
// Or with a file operation
let contents = std::fs::read_to_string(path)
.map_err(Error::into_internal_error);This wraps the original error’s message in an internal error, which is appropriate for unexpected failures. For expected error conditions that should be communicated to the peer, create specific error types instead.
§Error Types
Error is the same type on v1 and v2 connections. It holds
exactly what a JSON-RPC error object carries: a code,
a message, and optional data.
It provides factory methods for the codes that mean the same in every protocol version:
Error::parse_error()--32700, invalid JSONError::invalid_request()--32600, malformed requestError::method_not_found()--32601, unknown methodError::invalid_params()--32602, bad parametersError::internal_error()--32603, server errorError::request_cancelled()--32800, the request was cancelledError::auth_required()--32000, the request needs authentication first; a failed or abandoned v2auth/loginalso uses-32000
Errors whose data differs between protocol versions are built with the
schema version’s constructors and converted with .into():
-32002for a missing resource:v1::schema::Error::resource_not_found(uri)takes an optionaluri, andv2::schema::Error::resource_not_found()takes none-32002withdata.sessionId, for a session-scoped request naming a session that does not exist or is not active on the connection:v1::schema::Error::session_not_found(id)orv2::schema::Error::session_not_found(id)-32022for an agent that supports no protocol version at or below the requested one:v2::schema::Error::unsupported_protocol_version(requested, supported)(protocol v2 only)
use agent_client_protocol::{Error, v1::schema};
return Err(schema::Error::session_not_found(session_id).into());Compare codes with ErrorCode’s constants, or with a
schema version’s ErrorCode enum. v1::schema::Error::from(error) and
v2::schema::Error::from(error) convert back without losing anything, which is
how to read typed v2 error data such as SessionNotFoundErrorData.
Codes from -32768 to -32000 and from -32899 to -32800 are reserved
for JSON-RPC and ACP. Implementation-specific errors should use codes
outside both ranges, and receivers treat an unrecognized code as a generic
failure.
See the protocol’s error pages for when each code applies: v1 and v2.
You can add context with .data():
let error = agent_client_protocol::Error::invalid_params()
.data(serde_json::json!({
"field": "timeout",
"reason": "must be positive"
}));§Summary
| Situation | What to do |
|---|---|
| Send error response to request | responder.respond_with_error(error) |
| Handle notification failure | Log or return the error; the SDK sends no reply |
| Fail connection lifecycle work | Return Err(error) from a lifecycle callback |
| Convert external error | .map_err(Error::into_internal_error)? |