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 = "GCP_FEDERATION_INVALID",
13 message = "Invalid GCP federation field '{field}': {reason}",
14 retryable = "false",
15 internal = "false",
16 http_status_code = 400
17 )]
18 GcpFederationInvalid { field: String, reason: String },
19
20 #[error(
22 code = "SANDBOX_IMAGE_USER_INVALID",
23 message = "Invalid sandbox image USER '{user}': {reason}",
24 retryable = "false",
25 internal = "false",
26 http_status_code = 400
27 )]
28 SandboxImageUserInvalid { user: String, reason: String },
29 #[error(
31 code = "SANDBOX_EGRESS_POLICY_TOO_LARGE",
32 message = "Sandbox egress policy exceeds {limit}",
33 retryable = "false",
34 internal = "false",
35 http_status_code = 400
36 )]
37 SandboxEgressPolicyTooLarge { limit: String },
38
39 #[error(
41 code = "SANDBOX_EGRESS_INVALID",
42 message = "Invalid sandbox egress target '{target}': {reason}",
43 retryable = "false",
44 internal = "false",
45 http_status_code = 400
46 )]
47 SandboxEgressInvalid { target: String, reason: String },
48
49 #[error(
51 code = "SANDBOX_INTERNET_GATEWAY_MISSING",
52 message = "Sandbox '{sandbox_id}' declares egress 'internet', which routes through the managed sandbox egress gateway, but this stack has no gateway. Prepare the stack with the deployment preflights before rendering its setup template",
53 retryable = "false",
54 internal = "false"
55 )]
56 SandboxInternetGatewayMissing { sandbox_id: String },
57
58 #[error(
60 code = "CONTAINER_RESOURCE_SELECTION_INVALID",
61 message = "Invalid {dimension} selection for container '{resource_id}': {reason}",
62 retryable = "false",
63 internal = "false",
64 http_status_code = 400
65 )]
66 ContainerResourceSelectionInvalid {
67 resource_id: String,
68 dimension: String,
69 reason: String,
70 },
71 #[error(
73 code = "GENERIC_ERROR",
74 message = "{message}",
75 retryable = "true",
76 internal = "false"
77 )]
78 GenericError {
79 message: String,
81 },
82
83 #[error(
85 code = "UNEXPECTED_RESOURCE_TYPE",
86 message = "Unexpected resource type for resource '{resource_id}': expected {expected}, but got {actual}",
87 retryable = "false",
88 internal = "false"
89 )]
90 UnexpectedResourceType {
91 resource_id: String,
92 expected: ResourceType,
93 actual: ResourceType,
94 },
95
96 #[error(
98 code = "INVALID_RESOURCE_UPDATE",
99 message = "Resource '{resource_id}' cannot be updated: {reason}",
100 retryable = "false",
101 internal = "false"
102 )]
103 InvalidResourceUpdate { resource_id: String, reason: String },
104
105 #[error(
107 code = "WORKER_TIMEOUT_INVALID",
108 message = "Worker timeoutSeconds must be between 1 and {max_timeout_seconds} seconds, got {timeout_seconds}",
109 retryable = "false",
110 internal = "false",
111 http_status_code = 400
112 )]
113 WorkerTimeoutInvalid {
114 timeout_seconds: u32,
115 max_timeout_seconds: u32,
116 },
117
118 #[error(
120 code = "RESOURCE_HAS_NO_OUTPUTS",
121 message = "Resource '{resource_id}' has no outputs",
122 retryable = "true",
123 internal = "false"
124 )]
125 ResourceHasNoOutputs { resource_id: String },
126
127 #[error(
129 code = "RESOURCE_NOT_FOUND",
130 message = "Resource '{resource_id}' not found in stack state. Available resources: {available_resources:?}",
131 retryable = "false",
132 internal = "false",
133 http_status_code = 404
134 )]
135 ResourceNotFound {
136 resource_id: String,
137 available_resources: Vec<String>,
138 },
139
140 #[error(
142 code = "BINDING_CONFIG_INVALID",
143 message = "Invalid binding configuration for '{binding_name}': {reason}",
144 retryable = "false",
145 internal = "false"
146 )]
147 BindingConfigInvalid {
148 binding_name: String,
149 reason: String,
150 },
151
152 #[error(
154 code = "BINDING_ENV_VAR_MISSING",
155 message = "Missing environment variable '{env_var}' for binding '{binding_name}'",
156 retryable = "false",
157 internal = "false"
158 )]
159 BindingEnvVarMissing {
160 binding_name: String,
161 env_var: String,
162 },
163
164 #[error(
166 code = "BINDING_JSON_PARSE_FAILED",
167 message = "Failed to parse binding JSON for '{binding_name}': {reason}",
168 retryable = "false",
169 internal = "false"
170 )]
171 BindingJsonParseFailed {
172 binding_name: String,
173 reason: String,
174 },
175
176 #[error(
178 code = "UNEXPECTED_RESOURCE_STATUS_COMBINATION",
179 message = "Unexpected resource status combination during {operation}: {resource_statuses:?}",
180 retryable = "false",
181 internal = "true"
182 )]
183 UnexpectedResourceStatusCombination {
184 resource_statuses: Vec<String>,
185 operation: String,
186 },
187
188 #[error(
190 code = "EXTERNAL_BINDING_TYPE_MISMATCH",
191 message = "External binding type mismatch for resource '{resource_id}': expected {expected}, got {actual}",
192 retryable = "false",
193 internal = "false"
194 )]
195 ExternalBindingTypeMismatch {
196 resource_id: String,
197 expected: String,
198 actual: String,
199 },
200
201 #[error(
204 code = "PRESIGNED_REQUEST_EXPIRED",
205 message = "Presigned request for '{path}' expired at {expired_at}",
206 retryable = "false",
207 internal = "false"
208 )]
209 PresignedRequestExpired {
210 path: String,
211 expired_at: chrono::DateTime<chrono::Utc>,
212 },
213
214 #[error(
216 code = "HTTP_REQUEST_FAILED",
217 message = "HTTP {method} request to '{url}' failed",
218 retryable = "true",
219 internal = "false"
220 )]
221 HttpRequestFailed { url: String, method: String },
222
223 #[error(
225 code = "OPERATION_NOT_SUPPORTED",
226 message = "Operation '{operation}' not supported: {reason}",
227 retryable = "false",
228 internal = "false"
229 )]
230 OperationNotSupported { operation: String, reason: String },
231
232 #[error(
234 code = "LOCAL_FILESYSTEM_ERROR",
235 message = "Local filesystem error for '{path}' during {operation}",
236 retryable = "false",
237 internal = "false"
238 )]
239 LocalFilesystemError { path: String, operation: String },
240
241 #[error(
243 code = "FEATURE_NOT_ENABLED",
244 message = "Feature '{feature}' is not enabled",
245 retryable = "false",
246 internal = "false"
247 )]
248 FeatureNotEnabled { feature: String },
249
250 #[error(
252 code = "AUTOMATIC_SETUP_RESOURCE_UNSUPPORTED",
253 message = "Automatic infrastructure management does not support {resource_type} '{resource_id}' on {platform}; use manual setup",
254 retryable = "false",
255 internal = "false",
256 http_status_code = 400
257 )]
258 AutomaticSetupResourceUnsupported {
259 resource_type: String,
260 resource_id: String,
261 platform: Platform,
262 },
263
264 #[error(
266 code = "SANDBOX_CAPABILITY_UNSUPPORTED",
267 message = "Sandbox capability '{capability}' is not supported on {platform}",
268 retryable = "false",
269 internal = "false",
270 http_status_code = 400
271 )]
272 SandboxCapabilityUnsupported {
273 capability: String,
275 platform: String,
277 },
278
279 #[error(
281 code = "SANDBOX_PLATFORM_UNSUPPORTED",
282 message = "Sandbox is not available on {platform}",
283 retryable = "false",
284 internal = "false",
285 http_status_code = 400
286 )]
287 SandboxPlatformUnsupported {
288 platform: String,
290 },
291
292 #[error(
294 code = "BUNDLE_SIGNATURE_INVALID",
295 message = "Bundle signature invalid: {reason}",
296 retryable = "false",
297 internal = "false",
298 http_status_code = 400
299 )]
300 BundleSignatureInvalid {
301 reason: String,
303 },
304
305 #[error(
307 code = "SANDBOX_CAPABILITY_REFUSED",
308 message = "Sandbox capability refused: {reason}",
309 retryable = "false",
310 internal = "false",
311 http_status_code = 403
312 )]
313 SandboxCapabilityRefused {
314 reason: String,
316 },
317
318 #[error(
320 code = "SANDBOX_LIMIT_INVALID",
321 message = "Sandbox '{resource_id}' has an invalid {field} limit '{value}': {reason}",
322 retryable = "false",
323 internal = "false",
324 http_status_code = 400
325 )]
326 SandboxLimitInvalid {
327 resource_id: String,
329 field: String,
331 value: String,
333 reason: String,
335 },
336
337 #[error(
340 code = "INVALID_ENVELOPE",
341 message = "Invalid Commands envelope: {message}",
342 retryable = "false",
343 internal = "false"
344 )]
345 InvalidEnvelope {
346 message: String,
347 field: Option<String>,
348 },
349
350 #[error(
353 code = "COMMAND_TOKEN_MISSING",
354 message = "Deployment has command-enabled resource '{resource_id}' but no deployment token to authenticate it: {reason}",
355 retryable = "false",
356 internal = "false"
357 )]
358 CommandTokenMissing { resource_id: String, reason: String },
359
360 #[error(
362 code = "PUBLIC_URL_INVALID",
363 message = "Invalid public URL for resource '{resource_id}': {reason}",
364 retryable = "false",
365 internal = "false",
366 http_status_code = 400
367 )]
368 PublicUrlInvalid { resource_id: String, reason: String },
369
370 #[error(
372 code = "JSON_SERIALIZATION_FAILED",
373 message = "Failed to serialize JSON: {reason}",
374 retryable = "false",
375 internal = "false"
376 )]
377 JsonSerializationFailed { reason: String },
378
379 #[error(
381 code = "JSON_DESERIALIZATION_FAILED",
382 message = "Failed to deserialize JSON: {reason}",
383 retryable = "false",
384 internal = "false"
385 )]
386 JsonDeserializationFailed { reason: String },
387
388 #[error(
390 code = "TEMPLATE_SERIALIZATION_FAILED",
391 message = "Failed to serialize {format} template: {reason}",
392 retryable = "false",
393 internal = "true"
394 )]
395 TemplateSerializationFailed { format: String, reason: String },
396
397 #[error(
399 code = "MANAGEMENT_EXTERNAL_ID_INVALID",
400 message = "Management ExternalId must be 32 to 128 ASCII letters and digits (got {length} characters)",
401 retryable = "false",
402 internal = "false",
403 http_status_code = 400
404 )]
405 ManagementExternalIdInvalid {
406 length: usize,
408 },
409
410 #[error(
412 code = "IMPORT_DATA_INVALID",
413 message = "Invalid ImportData for resource '{resource_id}' ({resource_type}) on {platform}",
414 retryable = "false",
415 internal = "false",
416 http_status_code = 400
417 )]
418 ImportDataInvalid {
419 resource_id: String,
420 resource_type: ResourceType,
421 platform: Platform,
422 },
423
424 #[error(
426 code = "IMPORT_REGISTRATION_MISSING",
427 message = "Missing {registration_kind} registration for resource type '{resource_type}' on {platform}",
428 retryable = "false",
429 internal = "true"
430 )]
431 ImportRegistrationMissing {
432 resource_type: ResourceType,
433 platform: Platform,
434 registration_kind: String,
435 },
436
437 #[error(
439 code = "IMPORT_SCHEMA_SERIALIZATION_FAILED",
440 message = "Failed to serialize ImportData schema",
441 retryable = "false",
442 internal = "true"
443 )]
444 ImportSchemaSerializationFailed,
445}
446
447pub type Result<T> = alien_error::Result<T, ErrorData>;