1use alien_error::AlienErrorData;
2use serde::{Deserialize, Serialize};
3
4use crate::{Platform, ResourceType};
5
6#[derive(Debug, Clone, AlienErrorData, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub enum ErrorData {
10 #[error(
12 code = "GENERIC_ERROR",
13 message = "{message}",
14 retryable = "true",
15 internal = "false"
16 )]
17 GenericError {
18 message: String,
20 },
21
22 #[error(
24 code = "UNEXPECTED_RESOURCE_TYPE",
25 message = "Unexpected resource type for resource '{resource_id}': expected {expected}, but got {actual}",
26 retryable = "false",
27 internal = "false"
28 )]
29 UnexpectedResourceType {
30 resource_id: String,
31 expected: ResourceType,
32 actual: ResourceType,
33 },
34
35 #[error(
37 code = "INVALID_RESOURCE_UPDATE",
38 message = "Resource '{resource_id}' cannot be updated: {reason}",
39 retryable = "false",
40 internal = "false"
41 )]
42 InvalidResourceUpdate { resource_id: String, reason: String },
43
44 #[error(
46 code = "WORKER_TIMEOUT_INVALID",
47 message = "Worker timeoutSeconds must be between 1 and {max_timeout_seconds} seconds, got {timeout_seconds}",
48 retryable = "false",
49 internal = "false",
50 http_status_code = 400
51 )]
52 WorkerTimeoutInvalid {
53 timeout_seconds: u32,
54 max_timeout_seconds: u32,
55 },
56
57 #[error(
59 code = "RESOURCE_HAS_NO_OUTPUTS",
60 message = "Resource '{resource_id}' has no outputs",
61 retryable = "true",
62 internal = "false"
63 )]
64 ResourceHasNoOutputs { resource_id: String },
65
66 #[error(
68 code = "RESOURCE_NOT_FOUND",
69 message = "Resource '{resource_id}' not found in stack state. Available resources: {available_resources:?}",
70 retryable = "false",
71 internal = "false",
72 http_status_code = 404
73 )]
74 ResourceNotFound {
75 resource_id: String,
76 available_resources: Vec<String>,
77 },
78
79 #[error(
81 code = "BINDING_CONFIG_INVALID",
82 message = "Invalid binding configuration for '{binding_name}': {reason}",
83 retryable = "false",
84 internal = "false"
85 )]
86 BindingConfigInvalid {
87 binding_name: String,
88 reason: String,
89 },
90
91 #[error(
93 code = "BINDING_ENV_VAR_MISSING",
94 message = "Missing environment variable '{env_var}' for binding '{binding_name}'",
95 retryable = "false",
96 internal = "false"
97 )]
98 BindingEnvVarMissing {
99 binding_name: String,
100 env_var: String,
101 },
102
103 #[error(
105 code = "BINDING_JSON_PARSE_FAILED",
106 message = "Failed to parse binding JSON for '{binding_name}': {reason}",
107 retryable = "false",
108 internal = "false"
109 )]
110 BindingJsonParseFailed {
111 binding_name: String,
112 reason: String,
113 },
114
115 #[error(
117 code = "UNEXPECTED_RESOURCE_STATUS_COMBINATION",
118 message = "Unexpected resource status combination during {operation}: {resource_statuses:?}",
119 retryable = "false",
120 internal = "true"
121 )]
122 UnexpectedResourceStatusCombination {
123 resource_statuses: Vec<String>,
124 operation: String,
125 },
126
127 #[error(
129 code = "EXTERNAL_BINDING_TYPE_MISMATCH",
130 message = "External binding type mismatch for resource '{resource_id}': expected {expected}, got {actual}",
131 retryable = "false",
132 internal = "false"
133 )]
134 ExternalBindingTypeMismatch {
135 resource_id: String,
136 expected: String,
137 actual: String,
138 },
139
140 #[error(
143 code = "PRESIGNED_REQUEST_EXPIRED",
144 message = "Presigned request for '{path}' expired at {expired_at}",
145 retryable = "false",
146 internal = "false"
147 )]
148 PresignedRequestExpired {
149 path: String,
150 expired_at: chrono::DateTime<chrono::Utc>,
151 },
152
153 #[error(
155 code = "HTTP_REQUEST_FAILED",
156 message = "HTTP {method} request to '{url}' failed",
157 retryable = "true",
158 internal = "false"
159 )]
160 HttpRequestFailed { url: String, method: String },
161
162 #[error(
164 code = "OPERATION_NOT_SUPPORTED",
165 message = "Operation '{operation}' not supported: {reason}",
166 retryable = "false",
167 internal = "false"
168 )]
169 OperationNotSupported { operation: String, reason: String },
170
171 #[error(
173 code = "LOCAL_FILESYSTEM_ERROR",
174 message = "Local filesystem error for '{path}' during {operation}",
175 retryable = "false",
176 internal = "false"
177 )]
178 LocalFilesystemError { path: String, operation: String },
179
180 #[error(
182 code = "FEATURE_NOT_ENABLED",
183 message = "Feature '{feature}' is not enabled",
184 retryable = "false",
185 internal = "false"
186 )]
187 FeatureNotEnabled { feature: String },
188
189 #[error(
192 code = "INVALID_ENVELOPE",
193 message = "Invalid Commands envelope: {message}",
194 retryable = "false",
195 internal = "false"
196 )]
197 InvalidEnvelope {
198 message: String,
199 field: Option<String>,
200 },
201
202 #[error(
205 code = "COMMAND_TOKEN_MISSING",
206 message = "Deployment has command-enabled resource '{resource_id}' but no deployment token to authenticate it: {reason}",
207 retryable = "false",
208 internal = "false"
209 )]
210 CommandTokenMissing { resource_id: String, reason: String },
211
212 #[error(
214 code = "PUBLIC_URL_INVALID",
215 message = "Invalid public URL for resource '{resource_id}': {reason}",
216 retryable = "false",
217 internal = "false",
218 http_status_code = 400
219 )]
220 PublicUrlInvalid { resource_id: String, reason: String },
221
222 #[error(
224 code = "JSON_SERIALIZATION_FAILED",
225 message = "Failed to serialize JSON: {reason}",
226 retryable = "false",
227 internal = "false"
228 )]
229 JsonSerializationFailed { reason: String },
230
231 #[error(
233 code = "JSON_DESERIALIZATION_FAILED",
234 message = "Failed to deserialize JSON: {reason}",
235 retryable = "false",
236 internal = "false"
237 )]
238 JsonDeserializationFailed { reason: String },
239
240 #[error(
242 code = "TEMPLATE_SERIALIZATION_FAILED",
243 message = "Failed to serialize {format} template: {reason}",
244 retryable = "false",
245 internal = "true"
246 )]
247 TemplateSerializationFailed { format: String, reason: String },
248
249 #[error(
251 code = "IMPORT_DATA_INVALID",
252 message = "Invalid ImportData for resource '{resource_id}' ({resource_type}) on {platform}",
253 retryable = "false",
254 internal = "false",
255 http_status_code = 400
256 )]
257 ImportDataInvalid {
258 resource_id: String,
259 resource_type: ResourceType,
260 platform: Platform,
261 },
262
263 #[error(
265 code = "IMPORT_REGISTRATION_MISSING",
266 message = "Missing {registration_kind} registration for resource type '{resource_type}' on {platform}",
267 retryable = "false",
268 internal = "true"
269 )]
270 ImportRegistrationMissing {
271 resource_type: ResourceType,
272 platform: Platform,
273 registration_kind: String,
274 },
275
276 #[error(
278 code = "IMPORT_SCHEMA_SERIALIZATION_FAILED",
279 message = "Failed to serialize ImportData schema",
280 retryable = "false",
281 internal = "true"
282 )]
283 ImportSchemaSerializationFailed,
284}
285
286pub type Result<T> = alien_error::Result<T, ErrorData>;