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::{McpToolName, TaskId};
11use systemprompt_models::StepId;
12use systemprompt_traits::{BoxedSource, MetadataValidationError, RepositoryError};
13use thiserror::Error;
14
15#[derive(Debug, Error)]
16pub enum ArtifactError {
17    #[error("Missing required field: {field}")]
18    MissingField { field: String },
19
20    #[error("Invalid tool response schema: expected {expected}, found keys: {actual_keys:?}")]
21    InvalidSchema {
22        expected: &'static str,
23        actual_keys: Vec<String>,
24        #[source]
25        source: serde_json::Error,
26    },
27
28    #[error("Metadata validation error: {0}")]
29    MetadataValidation(#[from] MetadataValidationError),
30
31    #[error(
32        "tool {tool_name} declares no x-artifact-type; add x-artifact-type to the tool output or \
33         its output schema"
34    )]
35    MissingArtifactType { tool_name: McpToolName },
36
37    #[error("artifact must be a JSON object, found {found}")]
38    ArtifactNotObject { found: &'static str },
39}
40
41/// The execution-step row(s) a failed write was addressing: one step, or
42/// every in-progress step of a task.
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub enum ExecutionStepTarget {
45    Step(StepId),
46    Task(TaskId),
47}
48
49impl std::fmt::Display for ExecutionStepTarget {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        match self {
52            Self::Step(step_id) => write!(f, "step {step_id}"),
53            Self::Task(task_id) => write!(f, "steps of task {task_id}"),
54        }
55    }
56}
57
58#[derive(Debug, Error)]
59pub enum AgentError {
60    #[error("Artifact error: {0}")]
61    Artifact(#[from] ArtifactError),
62
63    #[error("repository: {0}")]
64    Repository(#[from] RepositoryError),
65
66    #[error("execution {target} could not be written: {source}")]
67    ExecutionStepWrite {
68        target: ExecutionStepTarget,
69        #[source]
70        source: RepositoryError,
71    },
72
73    #[error("config: cors_allowed_origins must contain at least one valid origin")]
74    EmptyCorsAllowlist,
75
76    #[error("config: {context}: {source}")]
77    InvalidConfig {
78        context: String,
79        #[source]
80        source: BoxedSource,
81    },
82
83    #[error("http: {0}")]
84    Http(#[from] reqwest::Error),
85
86    #[error("agent not found: {0}")]
87    NotFound(String),
88
89    #[error("No available ports in range {min}-{max}")]
90    NoAvailablePort { min: u16, max: u16 },
91
92    #[error("io: {0}")]
93    Io(#[from] std::io::Error),
94
95    #[error("services config: {0}")]
96    ServicesConfig(#[from] systemprompt_loader::ConfigLoadError),
97}
98
99impl AgentError {
100    pub fn step_write(step_id: &StepId, source: impl Into<RepositoryError>) -> Self {
101        Self::ExecutionStepWrite {
102            target: ExecutionStepTarget::Step(step_id.clone()),
103            source: source.into(),
104        }
105    }
106
107    pub fn task_steps_write(task_id: &TaskId, source: impl Into<RepositoryError>) -> Self {
108        Self::ExecutionStepWrite {
109            target: ExecutionStepTarget::Task(task_id.clone()),
110            source: source.into(),
111        }
112    }
113
114    pub fn invalid_config<E>(context: impl Into<String>, source: E) -> Self
115    where
116        E: std::error::Error + Send + Sync + 'static,
117    {
118        Self::InvalidConfig {
119            context: context.into(),
120            source: Box::new(source),
121        }
122    }
123}
124
125impl From<sqlx::Error> for AgentError {
126    fn from(err: sqlx::Error) -> Self {
127        Self::Repository(err.into())
128    }
129}
130
131pub type AgentResult<T> = Result<T, AgentError>;