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(
191 code = "SANDBOX_CAPABILITY_UNSUPPORTED",
192 message = "Sandbox capability '{capability}' is not supported on {platform}",
193 retryable = "false",
194 internal = "false",
195 http_status_code = 400
196 )]
197 SandboxCapabilityUnsupported {
198 capability: String,
200 platform: String,
202 },
203
204 #[error(
206 code = "SANDBOX_PLATFORM_UNSUPPORTED",
207 message = "Sandbox is not available on {platform}",
208 retryable = "false",
209 internal = "false",
210 http_status_code = 400
211 )]
212 SandboxPlatformUnsupported {
213 platform: String,
215 },
216
217 #[error(
219 code = "BUNDLE_SIGNATURE_INVALID",
220 message = "Bundle signature invalid: {reason}",
221 retryable = "false",
222 internal = "false",
223 http_status_code = 400
224 )]
225 BundleSignatureInvalid {
226 reason: String,
228 },
229
230 #[error(
232 code = "SANDBOX_CAPABILITY_REFUSED",
233 message = "Sandbox capability refused: {reason}",
234 retryable = "false",
235 internal = "false",
236 http_status_code = 403
237 )]
238 SandboxCapabilityRefused {
239 reason: String,
241 },
242
243 #[error(
245 code = "SANDBOX_LIMIT_INVALID",
246 message = "Sandbox '{resource_id}' has an invalid {field} limit '{value}': {reason}",
247 retryable = "false",
248 internal = "false",
249 http_status_code = 400
250 )]
251 SandboxLimitInvalid {
252 resource_id: String,
254 field: String,
256 value: String,
258 reason: String,
260 },
261
262 #[error(
265 code = "INVALID_ENVELOPE",
266 message = "Invalid Commands envelope: {message}",
267 retryable = "false",
268 internal = "false"
269 )]
270 InvalidEnvelope {
271 message: String,
272 field: Option<String>,
273 },
274
275 #[error(
278 code = "COMMAND_TOKEN_MISSING",
279 message = "Deployment has command-enabled resource '{resource_id}' but no deployment token to authenticate it: {reason}",
280 retryable = "false",
281 internal = "false"
282 )]
283 CommandTokenMissing { resource_id: String, reason: String },
284
285 #[error(
287 code = "PUBLIC_URL_INVALID",
288 message = "Invalid public URL for resource '{resource_id}': {reason}",
289 retryable = "false",
290 internal = "false",
291 http_status_code = 400
292 )]
293 PublicUrlInvalid { resource_id: String, reason: String },
294
295 #[error(
297 code = "JSON_SERIALIZATION_FAILED",
298 message = "Failed to serialize JSON: {reason}",
299 retryable = "false",
300 internal = "false"
301 )]
302 JsonSerializationFailed { reason: String },
303
304 #[error(
306 code = "JSON_DESERIALIZATION_FAILED",
307 message = "Failed to deserialize JSON: {reason}",
308 retryable = "false",
309 internal = "false"
310 )]
311 JsonDeserializationFailed { reason: String },
312
313 #[error(
315 code = "TEMPLATE_SERIALIZATION_FAILED",
316 message = "Failed to serialize {format} template: {reason}",
317 retryable = "false",
318 internal = "true"
319 )]
320 TemplateSerializationFailed { format: String, reason: String },
321
322 #[error(
324 code = "IMPORT_DATA_INVALID",
325 message = "Invalid ImportData for resource '{resource_id}' ({resource_type}) on {platform}",
326 retryable = "false",
327 internal = "false",
328 http_status_code = 400
329 )]
330 ImportDataInvalid {
331 resource_id: String,
332 resource_type: ResourceType,
333 platform: Platform,
334 },
335
336 #[error(
338 code = "IMPORT_REGISTRATION_MISSING",
339 message = "Missing {registration_kind} registration for resource type '{resource_type}' on {platform}",
340 retryable = "false",
341 internal = "true"
342 )]
343 ImportRegistrationMissing {
344 resource_type: ResourceType,
345 platform: Platform,
346 registration_kind: String,
347 },
348
349 #[error(
351 code = "IMPORT_SCHEMA_SERIALIZATION_FAILED",
352 message = "Failed to serialize ImportData schema",
353 retryable = "false",
354 internal = "true"
355 )]
356 ImportSchemaSerializationFailed,
357}
358
359pub type Result<T> = alien_error::Result<T, ErrorData>;