Skip to main content

alien_core/
error.rs

1use alien_error::AlienErrorData;
2use serde::{Deserialize, Serialize};
3
4use crate::ResourceType;
5
6/// Core error data exposed by the `alien-core` crate.
7#[derive(Debug, Clone, AlienErrorData, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub enum ErrorData {
10    /// A fallback error when nothing more specific matches.
11    #[error(
12        code = "GENERIC_ERROR",
13        message = "{message}",
14        retryable = "true",
15        internal = "false"
16    )]
17    GenericError {
18        /// Human-readable description of the error.
19        message: String,
20    },
21
22    /// Resource type mismatch found during stack operations.
23    #[error(
24        code = "UNEXPECTED_RESOURCE_TYPE",
25        message = "Unexpected resource type for resource '{resource_id}': expected {expected}, but got {actual}",
26        retryable = "false",
27        internal = "false"
28    )]
29    UnexpectedResourceType {
30        resource_id: String,
31        expected: ResourceType,
32        actual: ResourceType,
33    },
34
35    /// Attempt to update a resource that does not support updates.
36    #[error(
37        code = "INVALID_RESOURCE_UPDATE",
38        message = "Resource '{resource_id}' cannot be updated: {reason}",
39        retryable = "false",
40        internal = "false"
41    )]
42    InvalidResourceUpdate { resource_id: String, reason: String },
43
44    /// The resource exists but has not produced any outputs yet.
45    #[error(
46        code = "RESOURCE_HAS_NO_OUTPUTS",
47        message = "Resource '{resource_id}' has no outputs",
48        retryable = "true",
49        internal = "false"
50    )]
51    ResourceHasNoOutputs { resource_id: String },
52
53    /// Requested resource absent from the stack state.
54    #[error(
55        code = "RESOURCE_NOT_FOUND",
56        message = "Resource '{resource_id}' not found in stack state. Available resources: {available_resources:?}",
57        retryable = "false",
58        internal = "false",
59        http_status_code = 404
60    )]
61    ResourceNotFound {
62        resource_id: String,
63        available_resources: Vec<String>,
64    },
65
66    /// Binding configuration is invalid or missing required fields.
67    #[error(
68        code = "BINDING_CONFIG_INVALID",
69        message = "Invalid binding configuration for '{binding_name}': {reason}",
70        retryable = "false",
71        internal = "false"
72    )]
73    BindingConfigInvalid {
74        binding_name: String,
75        reason: String,
76    },
77
78    /// Environment variable for binding is missing.
79    #[error(
80        code = "BINDING_ENV_VAR_MISSING",
81        message = "Missing environment variable '{env_var}' for binding '{binding_name}'",
82        retryable = "false",
83        internal = "false"
84    )]
85    BindingEnvVarMissing {
86        binding_name: String,
87        env_var: String,
88    },
89
90    /// Failed to parse binding JSON from environment variable.
91    #[error(
92        code = "BINDING_JSON_PARSE_FAILED",
93        message = "Failed to parse binding JSON for '{binding_name}': {reason}",
94        retryable = "false",
95        internal = "false"
96    )]
97    BindingJsonParseFailed {
98        binding_name: String,
99        reason: String,
100    },
101
102    /// Unexpected combination of resource statuses when computing stack status.
103    #[error(
104        code = "UNEXPECTED_RESOURCE_STATUS_COMBINATION",
105        message = "Unexpected resource status combination during {operation}: {resource_statuses:?}",
106        retryable = "false",
107        internal = "true"
108    )]
109    UnexpectedResourceStatusCombination {
110        resource_statuses: Vec<String>,
111        operation: String,
112    },
113
114    /// External binding type does not match the resource type.
115    #[error(
116        code = "EXTERNAL_BINDING_TYPE_MISMATCH",
117        message = "External binding type mismatch for resource '{resource_id}': expected {expected}, got {actual}",
118        retryable = "false",
119        internal = "false"
120    )]
121    ExternalBindingTypeMismatch {
122        resource_id: String,
123        expected: String,
124        actual: String,
125    },
126
127    // Presigned request errors
128    /// Presigned request has expired.
129    #[error(
130        code = "PRESIGNED_REQUEST_EXPIRED",
131        message = "Presigned request for '{path}' expired at {expired_at}",
132        retryable = "false",
133        internal = "false"
134    )]
135    PresignedRequestExpired {
136        path: String,
137        expired_at: chrono::DateTime<chrono::Utc>,
138    },
139
140    /// HTTP request failed.
141    #[error(
142        code = "HTTP_REQUEST_FAILED",
143        message = "HTTP {method} request to '{url}' failed",
144        retryable = "true",
145        internal = "false"
146    )]
147    HttpRequestFailed { url: String, method: String },
148
149    /// Operation not supported.
150    #[error(
151        code = "OPERATION_NOT_SUPPORTED",
152        message = "Operation '{operation}' not supported: {reason}",
153        retryable = "false",
154        internal = "false"
155    )]
156    OperationNotSupported { operation: String, reason: String },
157
158    /// Local filesystem error.
159    #[error(
160        code = "LOCAL_FILESYSTEM_ERROR",
161        message = "Local filesystem error for '{path}' during {operation}",
162        retryable = "false",
163        internal = "false"
164    )]
165    LocalFilesystemError { path: String, operation: String },
166
167    /// Feature not enabled.
168    #[error(
169        code = "FEATURE_NOT_ENABLED",
170        message = "Feature '{feature}' is not enabled",
171        retryable = "false",
172        internal = "false"
173    )]
174    FeatureNotEnabled { feature: String },
175
176    // ARC protocol errors
177    /// Invalid ARC envelope.
178    #[error(
179        code = "INVALID_ENVELOPE",
180        message = "Invalid ARC envelope: {message}",
181        retryable = "false",
182        internal = "false"
183    )]
184    InvalidEnvelope {
185        message: String,
186        field: Option<String>,
187    },
188
189    /// JSON serialization failed.
190    #[error(
191        code = "JSON_SERIALIZATION_FAILED",
192        message = "Failed to serialize JSON: {reason}",
193        retryable = "false",
194        internal = "false"
195    )]
196    JsonSerializationFailed { reason: String },
197}
198
199pub type Result<T> = alien_error::Result<T, ErrorData>;