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