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