Skip to main content

systemprompt_cloud/error/
mod.rs

1//! Public error type for `systemprompt-cloud`.
2//!
3//! All public APIs of this crate return [`CloudError`] (or
4//! [`CloudResult<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]` (`reqwest`, `std::io`,
10//! `serde_json`) so callers can use `?` transparently.
11
12use systemprompt_identifiers::TenantId;
13use thiserror::Error;
14
15mod messages;
16
17pub type CloudResult<T> = Result<T, CloudError>;
18
19#[derive(Debug, Error)]
20#[non_exhaustive]
21pub enum CloudError {
22    #[error("Authentication required.\n\nRun: systemprompt cloud login")]
23    NotAuthenticated,
24
25    #[error("Token expired.\n\nRun: systemprompt cloud login")]
26    TokenExpired,
27
28    #[error("No tenant configured.\n\nRun: systemprompt cloud setup")]
29    TenantNotConfigured,
30
31    #[error("No app configured.\n\nRun: systemprompt cloud setup")]
32    AppNotConfigured,
33
34    #[error(
35        "Profile required: {message}\n\nSet SYSTEMPROMPT_PROFILE or run 'systemprompt cloud \
36         config'"
37    )]
38    ProfileRequired { message: String },
39
40    #[error("Missing profile field: {field}\n\nAdd to your profile:\n{example}")]
41    MissingProfileField { field: String, example: String },
42
43    #[error("JWT decode error")]
44    JwtDecode,
45
46    #[error("Credentials file corrupted.\n\nRun: systemprompt cloud login")]
47    CredentialsCorrupted {
48        #[source]
49        source: serde_json::Error,
50    },
51
52    #[error("Tenants not synced.\n\nRun: systemprompt cloud login")]
53    TenantsNotSynced,
54
55    #[error("Tenants store corrupted.\n\nRun: systemprompt cloud login")]
56    TenantsStoreCorrupted {
57        #[source]
58        source: serde_json::Error,
59    },
60
61    #[error("Tenants store invalid: {message}")]
62    TenantsStoreInvalid { message: String },
63
64    #[error("Tenant '{}' not found.\n\nRun: systemprompt cloud config", tenant_id.as_str())]
65    TenantNotFound { tenant_id: TenantId },
66
67    #[error("API error: {message}")]
68    ApiError { message: String },
69
70    #[error(transparent)]
71    Network(#[from] reqwest::Error),
72
73    #[error(transparent)]
74    Io(#[from] std::io::Error),
75
76    #[error(transparent)]
77    Json(#[from] serde_json::Error),
78
79    #[error("Cloud API validation failed: {message}")]
80    ApiValidationFailed { message: String },
81
82    #[error("Cloud credentials file invalid: {message}")]
83    InvalidCredentials { message: String },
84
85    #[error("Cloud credentials file not found: {path}")]
86    CredentialsFileNotFound { path: String },
87
88    #[error("Credentials not initialized")]
89    CredentialsNotInitialized,
90
91    #[error("Credentials already initialized")]
92    CredentialsAlreadyInitialized,
93
94    #[error(
95        "Session file version mismatch: expected {min}-{max}, got {actual}. Delete {path} and \
96         retry."
97    )]
98    SessionVersionMismatch {
99        min: u32,
100        max: u32,
101        actual: u32,
102        path: String,
103    },
104
105    #[error("OAuth flow failed: {message}")]
106    OAuthFlow { message: String },
107
108    #[error("Checkout flow failed: {message}")]
109    CheckoutFlow { message: String },
110
111    #[error("SSE stream error: {message}")]
112    SseStream { message: String },
113
114    #[error("Provisioning failed: {message}")]
115    ProvisioningFailed { message: String },
116
117    #[error("Authentication failed. Please run 'systemprompt cloud login' again.")]
118    Unauthorized,
119
120    #[error("Request failed with status {status}: {body}")]
121    HttpStatus { status: u16, body: String },
122
123    #[error("{message}")]
124    Other { message: String },
125}
126
127impl CloudError {
128    pub fn other(message: impl Into<String>) -> Self {
129        Self::Other {
130            message: message.into(),
131        }
132    }
133}