systemprompt_agent/
error.rs1use 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#[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>;