Skip to main content

zeph_acp/
error.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4/// Errors produced by the ACP server and its subsystems.
5///
6/// Each variant corresponds to a distinct failure domain so callers can handle
7/// or propagate them appropriately.
8///
9/// # Examples
10///
11/// ```
12/// use zeph_acp::AcpError;
13///
14/// let err = AcpError::Transport("connection reset".to_owned());
15/// assert!(err.to_string().contains("transport error"));
16///
17/// let err = AcpError::TerminalTimeout { output: "partial".to_owned() };
18/// assert!(err.to_string().contains("timed out"));
19/// ```
20#[derive(Debug, thiserror::Error)]
21#[non_exhaustive]
22pub enum AcpError {
23    /// The underlying JSON-RPC transport (stdio, HTTP, WebSocket) encountered an I/O error.
24    #[error("transport error: {0}")]
25    Transport(String),
26
27    /// The connected IDE returned a protocol-level error response.
28    #[error("IDE returned error: {0}")]
29    ClientError(String),
30
31    /// The IDE did not advertise the required ACP capability.
32    #[error("capability not available: {0}")]
33    CapabilityUnavailable(String),
34
35    /// The internal async channel between the agent loop and the ACP handler was dropped.
36    ///
37    /// This typically means the session has already terminated.
38    #[error("channel closed")]
39    ChannelClosed,
40
41    /// A terminal command did not complete within the configured timeout.
42    ///
43    /// `output` contains whatever the terminal produced before the timeout.
44    #[error("terminal command timed out; partial output: {output}")]
45    TerminalTimeout { output: String },
46
47    /// The stdin data payload exceeds the 64 KiB limit.
48    #[error("stdin payload too large: {size} bytes (max 65536)")]
49    StdinTooLarge { size: usize },
50
51    /// The terminal's stdin channel was closed by the IDE before the write completed.
52    #[error("broken pipe: terminal stdin closed")]
53    BrokenPipe,
54
55    /// A `ResourceLink` URI could not be resolved (bad scheme, path traversal, SSRF, etc.).
56    #[error("resource link error: {0}")]
57    ResourceLink(String),
58
59    /// The requested LLM provider is disabled for this session.
60    ///
61    /// Returned when a session-level `providers/disable` call has been made for the given
62    /// provider and the agent loop tries to resolve it for the next turn.
63    #[error("provider disabled: {provider_id}")]
64    ProviderDisabled {
65        /// The identifier of the disabled provider.
66        provider_id: String,
67    },
68
69    /// The requested LLM provider does not exist in the global provider registry.
70    ///
71    /// Returned when `providers/set` or turn resolution references an unknown provider id.
72    #[error("provider not found: {provider_id}")]
73    ProviderNotFound {
74        /// The identifier of the unknown provider.
75        provider_id: String,
76    },
77}