Skip to main content

scv_protocol/
error.rs

1//! Stable error codes: why a request or turn failed ([`ErrorCode`]), and why
2//! a tool call did ([`ToolErrorKind`]).
3//!
4//! Both are closed lists on the wire, but a newer server may add a value. An
5//! unrecognized string parses as `Unknown` rather than failing the whole
6//! event, so a client keeps working and shows the event's message.
7
8use std::fmt;
9
10use serde::{Deserialize, Serialize};
11
12/// Why a request (`error`) or a turn (`turn.failed`) failed.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15#[non_exhaustive]
16pub enum ErrorCode {
17    /// The frame is not JSON, or not a message this server knows, such as a
18    /// `daemon.control` action it predates.
19    InvalidJson,
20    /// A message other than `initialize` came first.
21    NotInitialized,
22    /// The client speaks another protocol version; the connection closes.
23    VersionMismatch,
24    /// The message is well-formed but its values are refused.
25    InvalidRequest,
26    /// This endpoint does not offer the request, such as component
27    /// management on stdio.
28    Unsupported,
29    /// No session, or a different one, is running on this connection.
30    SessionNotFound,
31    /// The session must be idle for this request.
32    TurnActive,
33    /// The turn named is not running.
34    TurnNotFound,
35    /// The approval is unknown or already resolved.
36    ApprovalNotFound,
37    /// The session's queue is full.
38    QueueLimit,
39    /// The queued prompt is gone.
40    QueueNotFound,
41    /// The queued prompt's revision is stale.
42    QueueConflict,
43    /// A channel account could not be changed.
44    ComponentError,
45    /// A delegation control request named nothing, or an unknown handle.
46    DelegationError,
47    /// The daemon refused to schedule a restart.
48    RestartError,
49    /// The daemon could not ask the owner a question (no owner chat to ask
50    /// in, or one already waiting there), or does not know the one named.
51    ConfirmError,
52    /// The model provider failed the turn.
53    ProviderError,
54    /// The turn's history does not fit the model's context window.
55    ContextLimit,
56    /// The turn reached `agent.max_steps`.
57    StepLimit,
58    /// The turn alone exceeds the session's history limits.
59    HistoryLimit,
60    /// A response exceeded a size limit.
61    ResponseLimit,
62    /// A response asked for too many tool calls, or too large arguments.
63    ToolLimit,
64    /// A server invariant failed.
65    InternalError,
66    /// A code this client does not know, from a newer server.
67    #[serde(other)]
68    Unknown,
69}
70
71impl ErrorCode {
72    /// The code as it appears on the wire, such as `queue_limit`.
73    pub fn as_str(self) -> &'static str {
74        match self {
75            Self::InvalidJson => "invalid_json",
76            Self::NotInitialized => "not_initialized",
77            Self::VersionMismatch => "version_mismatch",
78            Self::InvalidRequest => "invalid_request",
79            Self::Unsupported => "unsupported",
80            Self::SessionNotFound => "session_not_found",
81            Self::TurnActive => "turn_active",
82            Self::TurnNotFound => "turn_not_found",
83            Self::ApprovalNotFound => "approval_not_found",
84            Self::QueueLimit => "queue_limit",
85            Self::QueueNotFound => "queue_not_found",
86            Self::QueueConflict => "queue_conflict",
87            Self::ComponentError => "component_error",
88            Self::DelegationError => "delegation_error",
89            Self::RestartError => "restart_error",
90            Self::ConfirmError => "confirm_error",
91            Self::ProviderError => "provider_error",
92            Self::ContextLimit => "context_limit",
93            Self::StepLimit => "step_limit",
94            Self::HistoryLimit => "history_limit",
95            Self::ResponseLimit => "response_limit",
96            Self::ToolLimit => "tool_limit",
97            Self::InternalError => "internal_error",
98            Self::Unknown => "unknown",
99        }
100    }
101}
102
103impl fmt::Display for ErrorCode {
104    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105        formatter.write_str(self.as_str())
106    }
107}
108
109/// Why a tool call failed (`tool.completed.error`). The model reads the
110/// call's output either way; this tells a client how to show it.
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
112#[serde(rename_all = "snake_case")]
113#[non_exhaustive]
114pub enum ToolErrorKind {
115    /// The approval policy or the user refused the call.
116    Denied,
117    /// The call was cancelled while it ran.
118    Cancelled,
119    /// The call's arguments were refused before it ran.
120    InvalidArguments,
121    /// The tool, or the agent it runs, could not be used: missing, signed
122    /// out, or its provider unreachable.
123    Unavailable,
124    /// A configured size, count, depth, or time limit stopped the call.
125    Limit,
126    /// The call ran and failed.
127    Failed,
128    /// The model named a tool the session does not have.
129    UnknownTool,
130    /// A kind this client does not know, from a newer server.
131    #[serde(other)]
132    Unknown,
133}
134
135impl ToolErrorKind {
136    /// The kind as it appears on the wire, such as `denied`.
137    pub(crate) fn as_str(self) -> &'static str {
138        match self {
139            Self::Denied => "denied",
140            Self::Cancelled => "cancelled",
141            Self::InvalidArguments => "invalid_arguments",
142            Self::Unavailable => "unavailable",
143            Self::Limit => "limit",
144            Self::Failed => "failed",
145            Self::UnknownTool => "unknown_tool",
146            Self::Unknown => "unknown",
147        }
148    }
149}
150
151impl fmt::Display for ToolErrorKind {
152    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
153        formatter.write_str(self.as_str())
154    }
155}