Skip to main content

protocol_droid/adapters/secondary/acp/
error.rs

1//! The `AcpPeerError` — every error the ACP stdio peer
2//! can surface to the application layer.
3//!
4//! # Why `thiserror`
5//!
6//! The peer is a library component; the consumer
7//! matches on the variants. A free-form `String`
8//! error would be a silent log line, not a typed
9//! signal. `thiserror`'s derive keeps the variants
10//! typed and the `Display` impl consistent across
11//! them.
12
13use std::time::Duration;
14
15use thiserror::Error;
16
17/// Every error the ACP stdio peer can produce.
18///
19/// The variants are mapped from the underlying
20/// `tokio::io::Error`, `serde_json::Error`, and the
21/// peer's own protocol errors. A consumer that
22/// matches on the variants knows what category of
23/// failure happened, not just that "something
24/// failed".
25#[derive(Debug, Error)]
26pub enum AcpPeerError {
27    /// The peer could not be spawned, or the
28    /// stdio pipes could not be wired up.
29    #[error("spawning the ACP peer failed: {0}")]
30    Spawn(#[source] std::io::Error),
31
32    /// Writing an envelope to the child's stdin
33    /// failed. The peer is unrecoverable: the
34    /// child's write side is broken and the
35    /// reader task is about to see EOF.
36    #[error("writing the envelope to the peer failed: {0}")]
37    Write(#[source] std::io::Error),
38
39    /// The reply from the child was not a
40    /// well-formed JSON-RPC envelope. The peer
41    /// is recoverable: future requests can still
42    /// succeed if the agent's framing is
43    /// consistent and this one frame was a
44    /// one-off.
45    #[error("the peer returned a malformed reply: {0}")]
46    Decode(#[source] serde_json::Error),
47
48    /// The reply was well-formed JSON but did
49    /// not match the JSON-RPC 2.0 shape the
50    /// peer expects (`id` must be present on a
51    /// reply, `result`/`error` exactly one).
52    #[error("the peer returned an ill-shaped reply: {0}")]
53    IllShapedReply(String),
54
55    /// The reply was not received within the
56    /// configured timeout. The default is 30s;
57    /// a consumer that knows the agent is slow
58    /// can override.
59    #[error("the peer did not reply within {0:?}")]
60    Timeout(Duration),
61
62    /// The reply was a JSON-RPC error. The error
63    /// code and message are part of the error;
64    /// the consumer is the one that decides
65    /// whether to retry, surface, or fail.
66    #[error("the peer returned a JSON-RPC error: code={code}, message={message}")]
67    JsonRpc {
68        /// The JSON-RPC error code. `0` is
69        /// reserved (the wire does not use it),
70        /// so the integer is always the agent's
71        /// own code or one of the standard
72        /// pre-defined codes.
73        code: i64,
74        /// The human-readable message the
75        /// agent attached. The peer does not
76        /// log it; the consumer decides what
77        /// to do.
78        message: String,
79    },
80}