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