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