Skip to main content

systemprompt_agent/
error.rs

1//! Typed error hierarchy for the `systemprompt-agent` crate.
2//!
3//! Public APIs return concrete `thiserror`-derived enums instead of
4//! `anyhow::Error` so that downstream callers can match on error variants
5//! without string parsing.
6
7use systemprompt_identifiers::TaskId;
8use thiserror::Error;
9
10#[derive(Debug, Error)]
11pub enum RowParseError {
12    #[error("Missing required field: {field}")]
13    MissingField { field: String },
14
15    #[error("Invalid datetime for field '{field}'")]
16    InvalidDatetime { field: String },
17
18    #[error("JSON parse error for field '{field}': {source}")]
19    JsonParse {
20        field: String,
21        #[source]
22        source: serde_json::Error,
23    },
24}
25
26#[derive(Debug, Error)]
27pub enum TaskError {
28    #[error("Task UUID missing from database row")]
29    MissingTaskUuid,
30
31    #[error("Agent name not found for task {task_id}")]
32    MissingAgentName { task_id: TaskId },
33
34    #[error("Context ID missing from database row")]
35    MissingContextId,
36
37    #[error("Invalid task state: {state}")]
38    InvalidTaskState { state: String },
39
40    #[error(transparent)]
41    RowParse(#[from] RowParseError),
42
43    #[error("Metadata parse error: {0}")]
44    InvalidMetadata(#[from] serde_json::Error),
45
46    #[error("Empty task ID provided")]
47    EmptyTaskId,
48
49    #[error("Invalid task ID format: {id}")]
50    InvalidTaskIdFormat { id: String },
51
52    #[error("Message ID missing from database row")]
53    MissingMessageId,
54
55    #[error("Tool name missing for tool execution")]
56    MissingToolName,
57
58    #[error("Tool call ID missing for tool execution")]
59    MissingCallId,
60
61    #[error("Created timestamp missing from database")]
62    MissingCreatedTimestamp,
63
64    #[error("Database error: {0}")]
65    Database(String),
66}
67
68#[derive(Debug, Error)]
69pub enum ContextError {
70    #[error("Context UUID missing from database row")]
71    MissingUuid,
72
73    #[error("Context name missing from database row")]
74    MissingName,
75
76    #[error("User ID missing from database row")]
77    MissingUserId,
78
79    #[error(transparent)]
80    RowParse(#[from] RowParseError),
81
82    #[error("Role serialization error: {0}")]
83    RoleSerialization(#[from] serde_json::Error),
84
85    #[error("Database error: {0}")]
86    Database(String),
87}
88
89#[derive(Debug, Error)]
90pub enum ArtifactError {
91    #[error("Artifact UUID missing from database row")]
92    MissingUuid,
93
94    #[error("Artifact type missing from database row")]
95    MissingType,
96
97    #[error("Context ID missing for artifact")]
98    MissingContextId,
99
100    #[error(transparent)]
101    RowParse(#[from] RowParseError),
102
103    #[error("Invalid tool response schema: expected {expected}, found keys: {actual_keys:?}")]
104    InvalidSchema {
105        expected: &'static str,
106        actual_keys: Vec<String>,
107        #[source]
108        source: serde_json::Error,
109    },
110
111    #[error("Metadata parse error: {0}")]
112    InvalidMetadata(#[from] serde_json::Error),
113
114    #[error("Database error: {0}")]
115    Database(String),
116
117    #[error("Transform error: {0}")]
118    Transform(String),
119
120    #[error("Metadata validation error: {0}")]
121    MetadataValidation(String),
122}
123
124#[derive(Debug, Error)]
125pub enum ProtocolError {
126    #[error("Tool name missing in tool call")]
127    MissingToolName,
128
129    #[error("Tool result error flag is required but was not provided")]
130    MissingErrorFlag,
131
132    #[error("Message ID missing")]
133    MissingMessageId,
134
135    #[error("Request ID missing")]
136    MissingRequestId,
137
138    #[error("Latency value missing or invalid")]
139    InvalidLatency,
140
141    #[error("Validation failed: {0}")]
142    ValidationFailed(String),
143
144    #[error("JSON parse error: {0}")]
145    JsonParse(#[from] serde_json::Error),
146
147    #[error("Database error: {0}")]
148    Database(String),
149}
150
151#[derive(Debug, Error)]
152pub enum AgentError {
153    #[error("Task error: {0}")]
154    Task(#[from] TaskError),
155
156    #[error("Context error: {0}")]
157    Context(#[from] ContextError),
158
159    #[error("Artifact error: {0}")]
160    Artifact(#[from] ArtifactError),
161
162    #[error("A2A protocol error: {0}")]
163    Protocol(#[from] ProtocolError),
164
165    #[error("Repository error: {0}")]
166    Repository(String),
167
168    #[error("Database error: {0}")]
169    Database(String),
170
171    #[error("repository init: {0}")]
172    Init(String),
173
174    #[error("server: {0}")]
175    Server(String),
176
177    #[error("webhook: {0}")]
178    Webhook(String),
179
180    #[error("config: {0}")]
181    Config(String),
182
183    #[error("http: {0}")]
184    Http(#[from] reqwest::Error),
185
186    #[error("agent not found: {0}")]
187    NotFound(String),
188
189    #[error("spawn failed: {0}")]
190    Spawn(String),
191
192    #[error("lifecycle: {0}")]
193    Lifecycle(String),
194
195    #[error("validation: {0}")]
196    Validation(String),
197
198    #[error("sqlx error: {0}")]
199    Sqlx(#[from] sqlx::Error),
200
201    #[error("io: {0}")]
202    Io(#[from] std::io::Error),
203
204    #[error("internal: {0}")]
205    Internal(String),
206
207    #[error("services config: {0}")]
208    ServicesConfig(#[from] systemprompt_loader::ConfigLoadError),
209}
210
211pub type AgentResult<T> = Result<T, AgentError>;
212
213impl From<AgentError> for systemprompt_traits::RepositoryError {
214    fn from(err: AgentError) -> Self {
215        match err {
216            AgentError::Sqlx(e) => Self::Database(Box::new(e)),
217            other => Self::Database(other.to_string().into()),
218        }
219    }
220}