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 = "CONTAINER_RESOURCE_SELECTION_INVALID",
13 message = "Invalid {dimension} selection for container '{resource_id}': {reason}",
14 retryable = "false",
15 internal = "false",
16 http_status_code = 400
17 )]
18 ContainerResourceSelectionInvalid {
19 resource_id: String,
20 dimension: String,
21 reason: String,
22 },
23 #[error(
25 code = "GENERIC_ERROR",
26 message = "{message}",
27 retryable = "true",
28 internal = "false"
29 )]
30 GenericError {
31 message: String,
33 },
34
35 #[error(
37 code = "UNEXPECTED_RESOURCE_TYPE",
38 message = "Unexpected resource type for resource '{resource_id}': expected {expected}, but got {actual}",
39 retryable = "false",
40 internal = "false"
41 )]
42 UnexpectedResourceType {
43 resource_id: String,
44 expected: ResourceType,
45 actual: ResourceType,
46 },
47
48 #[error(
50 code = "INVALID_RESOURCE_UPDATE",
51 message = "Resource '{resource_id}' cannot be updated: {reason}",
52 retryable = "false",
53 internal = "false"
54 )]
55 InvalidResourceUpdate { resource_id: String, reason: String },
56
57 #[error(
59 code = "WORKER_TIMEOUT_INVALID",
60 message = "Worker timeoutSeconds must be between 1 and {max_timeout_seconds} seconds, got {timeout_seconds}",
61 retryable = "false",
62 internal = "false",
63 http_status_code = 400
64 )]
65 WorkerTimeoutInvalid {
66 timeout_seconds: u32,
67 max_timeout_seconds: u32,
68 },
69
70 #[error(
72 code = "RESOURCE_HAS_NO_OUTPUTS",
73 message = "Resource '{resource_id}' has no outputs",
74 retryable = "true",
75 internal = "false"
76 )]
77 ResourceHasNoOutputs { resource_id: String },
78
79 #[error(
81 code = "RESOURCE_NOT_FOUND",
82 message = "Resource '{resource_id}' not found in stack state. Available resources: {available_resources:?}",
83 retryable = "false",
84 internal = "false",
85 http_status_code = 404
86 )]
87 ResourceNotFound {
88 resource_id: String,
89 available_resources: Vec<String>,
90 },
91
92 #[error(
94 code = "BINDING_CONFIG_INVALID",
95 message = "Invalid binding configuration for '{binding_name}': {reason}",
96 retryable = "false",
97 internal = "false"
98 )]
99 BindingConfigInvalid {
100 binding_name: String,
101 reason: String,
102 },
103
104 #[error(
106 code = "BINDING_ENV_VAR_MISSING",
107 message = "Missing environment variable '{env_var}' for binding '{binding_name}'",
108 retryable = "false",
109 internal = "false"
110 )]
111 BindingEnvVarMissing {
112 binding_name: String,
113 env_var: String,
114 },
115
116 #[error(
118 code = "BINDING_JSON_PARSE_FAILED",
119 message = "Failed to parse binding JSON for '{binding_name}': {reason}",
120 retryable = "false",
121 internal = "false"
122 )]
123 BindingJsonParseFailed {
124 binding_name: String,
125 reason: String,
126 },
127
128 #[error(
130 code = "UNEXPECTED_RESOURCE_STATUS_COMBINATION",
131 message = "Unexpected resource status combination during {operation}: {resource_statuses:?}",
132 retryable = "false",
133 internal = "true"
134 )]
135 UnexpectedResourceStatusCombination {
136 resource_statuses: Vec<String>,
137 operation: String,
138 },
139
140 #[error(
142 code = "EXTERNAL_BINDING_TYPE_MISMATCH",
143 message = "External binding type mismatch for resource '{resource_id}': expected {expected}, got {actual}",
144 retryable = "false",
145 internal = "false"
146 )]
147 ExternalBindingTypeMismatch {
148 resource_id: String,
149 expected: String,
150 actual: String,
151 },
152
153 #[error(
156 code = "PRESIGNED_REQUEST_EXPIRED",
157 message = "Presigned request for '{path}' expired at {expired_at}",
158 retryable = "false",
159 internal = "false"
160 )]
161 PresignedRequestExpired {
162 path: String,
163 expired_at: chrono::DateTime<chrono::Utc>,
164 },
165
166 #[error(
168 code = "HTTP_REQUEST_FAILED",
169 message = "HTTP {method} request to '{url}' failed",
170 retryable = "true",
171 internal = "false"
172 )]
173 HttpRequestFailed { url: String, method: String },
174
175 #[error(
177 code = "OPERATION_NOT_SUPPORTED",
178 message = "Operation '{operation}' not supported: {reason}",
179 retryable = "false",
180 internal = "false"
181 )]
182 OperationNotSupported { operation: String, reason: String },
183
184 #[error(
186 code = "LOCAL_FILESYSTEM_ERROR",
187 message = "Local filesystem error for '{path}' during {operation}",
188 retryable = "false",
189 internal = "false"
190 )]
191 LocalFilesystemError { path: String, operation: String },
192
193 #[error(
195 code = "FEATURE_NOT_ENABLED",
196 message = "Feature '{feature}' is not enabled",
197 retryable = "false",
198 internal = "false"
199 )]
200 FeatureNotEnabled { feature: String },
201
202 #[error(
204 code = "AUTOMATIC_SETUP_RESOURCE_UNSUPPORTED",
205 message = "Automatic infrastructure management does not support {resource_type} '{resource_id}' on {platform}; use manual setup",
206 retryable = "false",
207 internal = "false",
208 http_status_code = 400
209 )]
210 AutomaticSetupResourceUnsupported {
211 resource_type: String,
212 resource_id: String,
213 platform: Platform,
214 },
215
216 #[error(
218 code = "SANDBOX_CAPABILITY_UNSUPPORTED",
219 message = "Sandbox capability '{capability}' is not supported on {platform}",
220 retryable = "false",
221 internal = "false",
222 http_status_code = 400
223 )]
224 SandboxCapabilityUnsupported {
225 capability: String,
227 platform: String,
229 },
230
231 #[error(
233 code = "SANDBOX_PLATFORM_UNSUPPORTED",
234 message = "Sandbox is not available on {platform}",
235 retryable = "false",
236 internal = "false",
237 http_status_code = 400
238 )]
239 SandboxPlatformUnsupported {
240 platform: String,
242 },
243
244 #[error(
246 code = "BUNDLE_SIGNATURE_INVALID",
247 message = "Bundle signature invalid: {reason}",
248 retryable = "false",
249 internal = "false",
250 http_status_code = 400
251 )]
252 BundleSignatureInvalid {
253 reason: String,
255 },
256
257 #[error(
259 code = "SANDBOX_CAPABILITY_REFUSED",
260 message = "Sandbox capability refused: {reason}",
261 retryable = "false",
262 internal = "false",
263 http_status_code = 403
264 )]
265 SandboxCapabilityRefused {
266 reason: String,
268 },
269
270 #[error(
272 code = "SANDBOX_LIMIT_INVALID",
273 message = "Sandbox '{resource_id}' has an invalid {field} limit '{value}': {reason}",
274 retryable = "false",
275 internal = "false",
276 http_status_code = 400
277 )]
278 SandboxLimitInvalid {
279 resource_id: String,
281 field: String,
283 value: String,
285 reason: String,
287 },
288
289 #[error(
292 code = "INVALID_ENVELOPE",
293 message = "Invalid Commands envelope: {message}",
294 retryable = "false",
295 internal = "false"
296 )]
297 InvalidEnvelope {
298 message: String,
299 field: Option<String>,
300 },
301
302 #[error(
305 code = "COMMAND_TOKEN_MISSING",
306 message = "Deployment has command-enabled resource '{resource_id}' but no deployment token to authenticate it: {reason}",
307 retryable = "false",
308 internal = "false"
309 )]
310 CommandTokenMissing { resource_id: String, reason: String },
311
312 #[error(
314 code = "PUBLIC_URL_INVALID",
315 message = "Invalid public URL for resource '{resource_id}': {reason}",
316 retryable = "false",
317 internal = "false",
318 http_status_code = 400
319 )]
320 PublicUrlInvalid { resource_id: String, reason: String },
321
322 #[error(
324 code = "JSON_SERIALIZATION_FAILED",
325 message = "Failed to serialize JSON: {reason}",
326 retryable = "false",
327 internal = "false"
328 )]
329 JsonSerializationFailed { reason: String },
330
331 #[error(
333 code = "JSON_DESERIALIZATION_FAILED",
334 message = "Failed to deserialize JSON: {reason}",
335 retryable = "false",
336 internal = "false"
337 )]
338 JsonDeserializationFailed { reason: String },
339
340 #[error(
342 code = "TEMPLATE_SERIALIZATION_FAILED",
343 message = "Failed to serialize {format} template: {reason}",
344 retryable = "false",
345 internal = "true"
346 )]
347 TemplateSerializationFailed { format: String, reason: String },
348
349 #[error(
351 code = "IMPORT_DATA_INVALID",
352 message = "Invalid ImportData for resource '{resource_id}' ({resource_type}) on {platform}",
353 retryable = "false",
354 internal = "false",
355 http_status_code = 400
356 )]
357 ImportDataInvalid {
358 resource_id: String,
359 resource_type: ResourceType,
360 platform: Platform,
361 },
362
363 #[error(
365 code = "IMPORT_REGISTRATION_MISSING",
366 message = "Missing {registration_kind} registration for resource type '{resource_type}' on {platform}",
367 retryable = "false",
368 internal = "true"
369 )]
370 ImportRegistrationMissing {
371 resource_type: ResourceType,
372 platform: Platform,
373 registration_kind: String,
374 },
375
376 #[error(
378 code = "IMPORT_SCHEMA_SERIALIZATION_FAILED",
379 message = "Failed to serialize ImportData schema",
380 retryable = "false",
381 internal = "true"
382 )]
383 ImportSchemaSerializationFailed,
384}
385
386pub type Result<T> = alien_error::Result<T, ErrorData>;