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}