alien_commands/error.rs
1use alien_error::{AlienError, AlienErrorData};
2use serde::{Deserialize, Serialize};
3
4/// Errors that occur in the commands protocol.
5#[derive(Debug, Clone, AlienErrorData, Serialize, Deserialize)]
6#[serde(rename_all = "camelCase")]
7pub enum ErrorData {
8 /// Command validation failed or contains invalid data.
9 #[error(
10 code = "INVALID_COMMAND",
11 message = "Invalid command: {message}",
12 retryable = "false",
13 internal = "false",
14 http_status_code = 400
15 )]
16 InvalidCommand {
17 /// Human-readable description of what makes the command invalid
18 message: String,
19 },
20
21 /// Requested command ID was not found.
22 #[error(
23 code = "COMMAND_NOT_FOUND",
24 message = "Command '{command_id}' not found",
25 retryable = "false",
26 internal = "false",
27 http_status_code = 404
28 )]
29 CommandNotFound {
30 /// ID of the command that was not found
31 command_id: String,
32 },
33
34 /// Explicitly requested command target does not exist in the deployment
35 /// (or is not command-capable). Also returned for an empty resource id.
36 ///
37 /// 404 mirrors the crate's other lookup failures (COMMAND_NOT_FOUND,
38 /// LEASE_NOT_FOUND): the named resource does not exist.
39 #[error(
40 code = "COMMAND_TARGET_NOT_FOUND",
41 message = "Command target '{resource_id}' not found in deployment '{deployment_id}'",
42 retryable = "false",
43 internal = "false",
44 http_status_code = 404
45 )]
46 CommandTargetNotFound {
47 /// Resource ID that was requested but not found
48 resource_id: String,
49 /// Deployment the target was looked up in
50 deployment_id: String,
51 },
52
53 /// Single-target shorthand was used but the deployment has multiple
54 /// command-capable targets.
55 ///
56 /// 409 mirrors CONFLICT: the request conflicts with the deployment's
57 /// current state and the client resolves it by naming a target.
58 #[error(
59 code = "COMMAND_TARGET_AMBIGUOUS",
60 message = "Deployment '{deployment_id}' has multiple command-capable targets; specify targetResourceId",
61 retryable = "false",
62 internal = "false",
63 http_status_code = 409
64 )]
65 CommandTargetAmbiguous {
66 /// Deployment with more than one command-capable target
67 deployment_id: String,
68 },
69
70 /// The deployment has no command-capable targets at all.
71 ///
72 /// 422: the request is well-formed but unsatisfiable for this deployment
73 /// (no resource has commands enabled) — unlike 400 (malformed) or 404
74 /// (a specific named thing missing).
75 #[error(
76 code = "NO_COMMAND_TARGETS",
77 message = "Deployment '{deployment_id}' has no command-capable targets",
78 retryable = "false",
79 internal = "false",
80 http_status_code = 422
81 )]
82 NoCommandTargets {
83 /// Deployment without any command-capable targets
84 deployment_id: String,
85 },
86
87 /// A command target resource id contains `:`, which would break the
88 /// pending-index and idempotency-key delimiter grammar.
89 ///
90 /// 400: the request (or a stored target) is malformed. The commands layer
91 /// permits every resource-id character except `:`, which delimits key
92 /// segments.
93 #[error(
94 code = "COMMAND_TARGET_ID_INVALID",
95 message = "Command target id '{resource_id}' is invalid: ids cannot contain ':'",
96 retryable = "false",
97 internal = "false",
98 http_status_code = 400
99 )]
100 CommandTargetIdInvalid {
101 /// The offending resource id
102 resource_id: String,
103 },
104
105 /// Pull command receiver configuration from the environment is missing
106 /// or invalid (e.g. a required `ALIEN_COMMANDS_*` variable is absent).
107 #[error(
108 code = "COMMAND_RECEIVER_CONFIG_INVALID",
109 message = "Command receiver configuration invalid: {message}",
110 retryable = "false",
111 internal = "false",
112 http_status_code = 400
113 )]
114 CommandReceiverConfigInvalid {
115 /// Human-readable description of what is missing or invalid
116 message: String,
117 /// Environment variable that is missing or invalid
118 env_var: String,
119 },
120
121 /// The command receiver bearer token was rejected by the commands API.
122 #[error(
123 code = "COMMAND_RECEIVER_UNAUTHORIZED",
124 message = "Command receiver authorization failed during {operation}",
125 retryable = "false",
126 internal = "false",
127 http_status_code = 401
128 )]
129 CommandReceiverUnauthorized {
130 /// Command API operation that was rejected
131 operation: String,
132 /// Commands API URL that rejected the token
133 url: String,
134 },
135
136 /// The command receiver request was permanently rejected by the commands API.
137 #[error(
138 code = "COMMAND_RECEIVER_REQUEST_REJECTED",
139 message = "Command receiver request was rejected with HTTP {status} during {operation}",
140 retryable = "false",
141 internal = "false",
142 http_status_code = 502
143 )]
144 CommandReceiverRequestRejected {
145 /// Command API operation that was rejected
146 operation: String,
147 /// HTTP status returned by the commands API
148 status: u16,
149 /// Commands API URL that rejected the request
150 url: String,
151 },
152
153 /// Invalid state transition attempted on command.
154 #[error(
155 code = "INVALID_STATE_TRANSITION",
156 message = "Invalid state transition from '{from}' to '{to}'",
157 retryable = "false",
158 internal = "false",
159 http_status_code = 400
160 )]
161 InvalidStateTransition {
162 /// Current state of the command
163 from: String,
164 /// Attempted new state
165 to: String,
166 },
167
168 /// Command has expired and can no longer be processed.
169 #[error(
170 code = "COMMAND_EXPIRED",
171 message = "Command '{command_id}' has expired",
172 retryable = "false",
173 internal = "false",
174 http_status_code = 410
175 )]
176 CommandExpired {
177 /// ID of the expired command
178 command_id: String,
179 },
180
181 /// Storage backend operation failed.
182 #[error(
183 code = "STORAGE_OPERATION_FAILED",
184 message = "Storage operation failed: {message}",
185 retryable = "true",
186 internal = "false"
187 )]
188 StorageOperationFailed {
189 /// Human-readable description of the storage failure
190 message: String,
191 /// Storage operation type (upload, download, etc.)
192 operation: Option<String>,
193 /// Storage path or URL that failed
194 path: Option<String>,
195 },
196
197 /// Key-value store operation failed.
198 #[error(
199 code = "KV_OPERATION_FAILED",
200 message = "KV operation '{operation}' failed on key '{key}': {message}",
201 retryable = "true",
202 internal = "false"
203 )]
204 KvOperationFailed {
205 /// Type of KV operation that failed
206 operation: String,
207 /// Key that was being operated on
208 key: String,
209 /// Human-readable description of the failure
210 message: String,
211 },
212
213 /// Transport dispatch to agent failed.
214 #[error(
215 code = "TRANSPORT_DISPATCH_FAILED",
216 message = "Transport dispatch failed: {message}",
217 retryable = "true",
218 internal = "false"
219 )]
220 TransportDispatchFailed {
221 /// Human-readable description of the dispatch failure
222 message: String,
223 /// Transport type that failed
224 transport_type: Option<String>,
225 /// Target endpoint or agent identifier
226 target: Option<String>,
227 },
228
229 /// Transport rejected a command before accepting it for execution.
230 ///
231 /// Unlike an acknowledgement timeout, this is a definite non-delivery:
232 /// the command server may safely record a terminal delivery failure.
233 #[error(
234 code = "TRANSPORT_DISPATCH_REJECTED",
235 message = "Transport rejected dispatch: {message}",
236 retryable = "false",
237 internal = "false"
238 )]
239 TransportDispatchRejected {
240 /// Safe description that contains no endpoint credentials or body.
241 message: String,
242 /// Transport type that rejected the command.
243 transport_type: Option<String>,
244 /// Command identifier (never a credential-bearing URL).
245 target: Option<String>,
246 },
247
248 /// Command envelope validation or parsing failed.
249 #[error(
250 code = "INVALID_ENVELOPE",
251 message = "Invalid command envelope: {message}",
252 retryable = "false",
253 internal = "false",
254 http_status_code = 400
255 )]
256 InvalidEnvelope {
257 /// Human-readable description of the envelope issue
258 message: String,
259 /// Envelope field that caused the validation failure
260 field: Option<String>,
261 },
262
263 /// Agent reported an error during command processing.
264 #[error(
265 code = "AGENT_ERROR",
266 message = "Agent error: {message}",
267 retryable = "true",
268 internal = "false"
269 )]
270 AgentError {
271 /// Human-readable description of the agent error
272 message: String,
273 /// Agent identifier if available
274 deployment_id: Option<String>,
275 },
276
277 /// Serialization or deserialization operation failed.
278 #[error(
279 code = "SERIALIZATION_FAILED",
280 message = "Serialization failed: {message}",
281 retryable = "false",
282 internal = "true"
283 )]
284 SerializationFailed {
285 /// Human-readable description of the serialization failure
286 message: String,
287 /// Data type that failed to serialize/deserialize
288 data_type: Option<String>,
289 },
290
291 /// HTTP operation failed during command processing.
292 #[error(
293 code = "HTTP_OPERATION_FAILED",
294 message = "HTTP operation failed: {message}",
295 retryable = "true",
296 internal = "false"
297 )]
298 HttpOperationFailed {
299 /// Human-readable description of the HTTP failure
300 message: String,
301 /// HTTP method if available
302 method: Option<String>,
303 /// URL that failed if available
304 url: Option<String>,
305 },
306
307 /// Operation is not supported by the current configuration.
308 #[error(
309 code = "OPERATION_NOT_SUPPORTED",
310 message = "Operation not supported: {message}",
311 retryable = "false",
312 internal = "false",
313 http_status_code = 501
314 )]
315 OperationNotSupported {
316 /// Human-readable description of what operation is not supported
317 message: String,
318 /// Operation type that was attempted
319 operation: Option<String>,
320 },
321
322 /// Resource conflict detected (e.g., concurrent modification).
323 #[error(
324 code = "CONFLICT",
325 message = "Conflict: {message}",
326 retryable = "true",
327 internal = "false",
328 http_status_code = 409
329 )]
330 Conflict {
331 /// Human-readable description of the conflict
332 message: String,
333 /// Resource identifier that has the conflict
334 resource_id: Option<String>,
335 },
336
337 /// Requested lease ID was not found.
338 #[error(
339 code = "LEASE_NOT_FOUND",
340 message = "Lease '{lease_id}' not found",
341 retryable = "false",
342 internal = "false",
343 http_status_code = 404
344 )]
345 LeaseNotFound {
346 /// ID of the lease that was not found
347 lease_id: String,
348 },
349
350 /// Generic commands error for uncommon cases.
351 #[error(
352 code = "COMMANDS_ERROR",
353 message = "Commands protocol error: {message}",
354 retryable = "true",
355 internal = "true"
356 )]
357 Other {
358 /// Human-readable description of the error
359 message: String,
360 },
361}
362
363pub type Error = AlienError<ErrorData>;
364pub type Result<T> = alien_error::Result<T, ErrorData>;