Skip to main content

systemprompt_config/
error.rs

1//! Public error type for `systemprompt-config`.
2//!
3//! All public APIs of this crate return [`ConfigError`] (or
4//! [`ConfigResult<T>`]) instead of `anyhow::Error`. The enum is
5//! `#[non_exhaustive]` so additional variants can be added in patch
6//! releases without breaking downstream code that performs exhaustive
7//! matching only on the documented variants.
8//!
9//! Upstream errors are composed via `#[from]` so callers can use `?`
10//! transparently from `std::io`, `serde_json`, `serde_yaml`, and
11//! `regex` operations performed inside the bootstrap and validator
12//! pipelines.
13
14use std::path::PathBuf;
15
16use systemprompt_models::errors::SecretsError;
17use systemprompt_models::profile::{GatewayProfileError, ProfileError};
18
19use crate::bootstrap::{ProfileBootstrapError, SecretsBootstrapError};
20use crate::services::ConfigValidationError;
21
22pub type ConfigResult<T> = Result<T, ConfigError>;
23
24#[derive(Debug, thiserror::Error)]
25#[non_exhaustive]
26pub enum ConfigError {
27    #[error("Config already initialized")]
28    AlreadyInitialized,
29
30    #[error(transparent)]
31    Profile(#[from] ProfileBootstrapError),
32
33    #[error(transparent)]
34    Secrets(#[from] SecretsBootstrapError),
35
36    #[error(transparent)]
37    ProfileParse(#[from] ProfileError),
38
39    #[error(transparent)]
40    Gateway(#[from] GatewayProfileError),
41
42    #[error(transparent)]
43    SchemaValidation(#[from] ConfigValidationError),
44
45    #[error(transparent)]
46    SecretsParse(#[from] SecretsError),
47
48    #[error(transparent)]
49    Io(#[from] std::io::Error),
50
51    #[error(transparent)]
52    Json(#[from] serde_json::Error),
53
54    #[error(transparent)]
55    Yaml(#[from] serde_yaml::Error),
56
57    #[error("Missing required path: paths.{field}")]
58    MissingProfilePath { field: String },
59
60    #[error("Failed to canonicalize {name} path: {source}")]
61    CanonicalizePath {
62        name: String,
63        #[source]
64        source: std::io::Error,
65    },
66
67    #[error("Profile path '{field}' cannot be read: {path}")]
68    ReadProfilePath {
69        field: String,
70        path: PathBuf,
71        #[source]
72        source: std::io::Error,
73    },
74
75    #[error("Profile path '{field}' has invalid YAML: {path}")]
76    InvalidProfileYaml {
77        field: String,
78        path: PathBuf,
79        #[source]
80        source: serde_yaml::Error,
81    },
82
83    #[error("Profile path validation failed: {message}")]
84    ProfilePathReport { message: String },
85
86    #[error("Unsupported database type '{db_type}'. Only 'postgres' is supported.")]
87    UnsupportedDatabaseType { db_type: String },
88
89    #[error("Invalid database URL: {message}")]
90    InvalidDatabaseUrl { message: String },
91
92    #[error("Failed to resolve variables after {passes} passes: {unresolved}")]
93    UnresolvedVariables { passes: usize, unresolved: String },
94
95    #[error("{count} validation error(s)")]
96    ValidationErrors { count: usize },
97
98    #[error("Required config file missing: {path}")]
99    EnvironmentConfigMissing { path: PathBuf },
100
101    #[error(
102        "Profile is missing required `system_admin.username` and `SYSTEMPROMPT_SYSTEM_ADMIN` is \
103         not set. The platform refuses to start without an explicit system-admin identity."
104    )]
105    MissingSystemAdmin,
106
107    #[error("{message}")]
108    Other { message: String },
109}
110
111impl ConfigError {
112    pub fn other(message: impl Into<String>) -> Self {
113        Self::Other {
114            message: message.into(),
115        }
116    }
117}