systemprompt_api/error/
conversions.rs1use systemprompt_agent::AgentError;
12use systemprompt_config::{ProfileBootstrapError, SecretsBootstrapError};
13use systemprompt_content::ContentError;
14use systemprompt_loader::{BundleError, ConfigLoadError};
15use systemprompt_marketplace::MarketplaceError;
16use systemprompt_marketplace::managed::ManagedError;
17use systemprompt_models::api::ApiError;
18use systemprompt_models::errors::GlobalConfigError;
19use systemprompt_models::execution::ContextExtractionError;
20use systemprompt_oauth::services::SessionCreationError;
21use systemprompt_oauth::{OauthError, OauthErrorKind};
22use systemprompt_security::authz::{AuthzError, ScopeBindingError};
23use systemprompt_traits::RepositoryError;
24use systemprompt_users::UserError;
25
26use super::ApiHttpError;
27
28impl From<RepositoryError> for ApiHttpError {
29 fn from(err: RepositoryError) -> Self {
30 Self(ApiError::from(err))
31 }
32}
33
34impl From<AuthzError> for ApiHttpError {
35 fn from(err: AuthzError) -> Self {
36 Self(ApiError::internal("Authorization lookup failed", err))
37 }
38}
39
40impl From<ScopeBindingError> for ApiHttpError {
41 fn from(err: ScopeBindingError) -> Self {
42 match err {
43 ScopeBindingError::UnknownDimension(_) => Self::bad_request(err.to_string()),
44 ScopeBindingError::NotAMember { .. } => Self::forbidden(err.to_string()),
45 ScopeBindingError::Lookup(source) => Self::from(source),
46 }
47 }
48}
49
50impl From<ConfigLoadError> for ApiHttpError {
51 fn from(err: ConfigLoadError) -> Self {
52 Self(ApiError::internal(
53 "Services configuration unavailable",
54 err,
55 ))
56 }
57}
58
59impl From<ProfileBootstrapError> for ApiHttpError {
60 fn from(err: ProfileBootstrapError) -> Self {
61 Self(ApiError::internal("Profile not ready", err))
62 }
63}
64
65impl From<SecretsBootstrapError> for ApiHttpError {
66 fn from(err: SecretsBootstrapError) -> Self {
67 Self(ApiError::internal("Secrets not ready", err))
68 }
69}
70
71impl From<GlobalConfigError> for ApiHttpError {
72 fn from(err: GlobalConfigError) -> Self {
73 Self(ApiError::internal("Configuration not ready", err))
74 }
75}
76
77impl From<AgentError> for ApiHttpError {
78 fn from(err: AgentError) -> Self {
79 let api = match err {
80 AgentError::NotFound(msg) => ApiError::not_found(msg),
81 AgentError::Repository(inner) => ApiError::from(inner),
82 other => ApiError::internal("Agent operation failed", other),
83 };
84 Self(api)
85 }
86}
87
88impl From<ContentError> for ApiHttpError {
89 fn from(err: ContentError) -> Self {
90 let api = match err {
91 ContentError::Repository(inner) => ApiError::from(inner),
92 e @ (ContentError::ContentNotFound(_) | ContentError::LinkNotFound(_)) => {
93 ApiError::not_found(e.to_string())
94 },
95 e @ (ContentError::InvalidRequest(_) | ContentError::Validation(_)) => {
96 ApiError::bad_request(e.to_string())
97 },
98 other => ApiError::internal("Content operation failed", other),
99 };
100 Self(api)
101 }
102}
103
104impl From<MarketplaceError> for ApiHttpError {
105 fn from(err: MarketplaceError) -> Self {
106 let api = match err {
107 MarketplaceError::Managed(ManagedError::Repository(inner)) => ApiError::from(inner),
108 e @ (MarketplaceError::NotFound(_)
109 | MarketplaceError::NoDefault
110 | MarketplaceError::Managed(ManagedError::Unavailable)) => {
111 ApiError::not_found(e.to_string())
112 },
113 e @ MarketplaceError::Managed(
114 ManagedError::Invalid(_) | ManagedError::InvalidInput { .. },
115 ) => ApiError::bad_request(e.to_string()),
116 e @ MarketplaceError::Managed(ManagedError::Conflict(_)) => {
117 ApiError::conflict(e.to_string())
118 },
119 e @ (MarketplaceError::Catalog(_)
120 | MarketplaceError::CatalogSource { .. }
121 | MarketplaceError::Managed(_)
122 | MarketplaceError::Import { .. }
123 | MarketplaceError::ImportSource { .. }
124 | MarketplaceError::Signing(_)
125 | MarketplaceError::Filter(_)) => ApiError::internal("Marketplace operation failed", e),
126 };
127 Self(api)
128 }
129}
130
131impl From<UserError> for ApiHttpError {
132 fn from(err: UserError) -> Self {
133 let api = match err {
134 UserError::Repository(inner) => ApiError::from(inner),
135 e @ UserError::NotFound(_) => ApiError::not_found(e.to_string()),
136 e @ UserError::EmailAlreadyExists(_) => ApiError::conflict(e.to_string()),
137 e @ (UserError::Validation(_)
138 | UserError::InvalidStatus(_)
139 | UserError::InvalidRole(_)
140 | UserError::InvalidRoles(_)) => ApiError::bad_request(e.to_string()),
141 e @ (UserError::MergeUnavailable
142 | UserError::PurgeIdentifier { .. }
143 | UserError::OwnerReassignment { .. }) => {
144 ApiError::internal("User operation failed", e)
145 },
146 };
147 Self(api)
148 }
149}
150
151impl From<OauthError> for ApiHttpError {
152 fn from(err: OauthError) -> Self {
153 if matches!(
154 err,
155 OauthError::CodeNotFound(_)
156 | OauthError::TokenNotFound(_)
157 | OauthError::ClientNotFound(_)
158 | OauthError::UserNotFound(_)
159 ) {
160 return Self(ApiError::not_found(err.to_string()));
161 }
162 let api = match err.kind() {
163 OauthErrorKind::InvalidRequest
164 | OauthErrorKind::InvalidClientMetadata
165 | OauthErrorKind::ExpiredChallenge
166 | OauthErrorKind::InvalidCredential => ApiError::bad_request(err.to_string()),
167 OauthErrorKind::UsernameUnavailable | OauthErrorKind::EmailExists => {
168 ApiError::conflict(err.to_string())
169 },
170 OauthErrorKind::InvalidClient => ApiError::unauthorized("Client authentication failed"),
171 OauthErrorKind::AuthenticationFailed => ApiError::unauthorized("Authentication failed"),
172 OauthErrorKind::InvalidGrant
173 | OauthErrorKind::InvalidToken
174 | OauthErrorKind::AccessDenied => ApiError::unauthorized(err.to_string()),
175 OauthErrorKind::NotFound => ApiError::not_found(err.to_string()),
176 OauthErrorKind::ServerError => {
177 ApiError::internal("Authorization operation failed", err)
178 },
179 };
180 Self(api)
181 }
182}
183
184impl From<SessionCreationError> for ApiHttpError {
185 fn from(err: SessionCreationError) -> Self {
186 Self(match err {
187 e @ SessionCreationError::UserNotFound { .. } => ApiError::not_found(e.to_string()),
188 other => ApiError::internal("Session creation failed", other),
189 })
190 }
191}
192
193impl From<ContextExtractionError> for ApiHttpError {
194 fn from(err: ContextExtractionError) -> Self {
195 let api = match err {
196 ContextExtractionError::InvalidToken(source) => {
197 ApiError::unauthorized("Invalid or expired token").with_source(source)
198 },
199 e @ (ContextExtractionError::MissingHeader(_)
200 | ContextExtractionError::MissingAuthHeader
201 | ContextExtractionError::Revoked
202 | ContextExtractionError::MissingSessionId
203 | ContextExtractionError::MissingUserId) => ApiError::unauthorized(e.to_string()),
204 e @ (ContextExtractionError::MissingContextId
205 | ContextExtractionError::InvalidHeaderValue { .. }
206 | ContextExtractionError::InvalidUserId(_)) => ApiError::bad_request(e.to_string()),
207 e @ ContextExtractionError::ForbiddenHeader { .. } => {
208 ApiError::forbidden(e.to_string())
209 },
210 e @ ContextExtractionError::UserNotFound(_) => ApiError::not_found(e.to_string()),
211 e @ ContextExtractionError::DatabaseError { .. } => {
212 ApiError::internal("Request context lookup failed", e)
213 },
214 };
215 Self(api)
216 }
217}
218
219impl From<BundleError> for ApiHttpError {
220 fn from(err: BundleError) -> Self {
221 let api = match err {
222 e @ BundleError::Auth { .. } => ApiError::forbidden(e.to_string()),
223 e @ (BundleError::Verify(_)
224 | BundleError::Ownership { .. }
225 | BundleError::Policy { .. }
226 | BundleError::TooLarge { .. }) => ApiError::bad_request(e.to_string()),
227 e @ BundleError::SourceMissing { .. } => ApiError::not_found(e.to_string()),
228 e @ (BundleError::Fetch { .. } | BundleError::Extract { .. } | BundleError::Io(_)) => {
229 ApiError::internal("Services bundle operation failed", e)
230 },
231 };
232 Self(api)
233 }
234}