error-envelope 0.3.0

Structured, consistent error responses for Rust APIs. Framework-agnostic with Axum support.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# Error Codes Reference

Complete reference of all error codes in error-envelope.

## Overview

error-envelope provides 18 standard error codes as a type-safe enum. Each code has:
- **Default HTTP status** - The standard status code for this error type
- **Default retryable behavior** - Whether clients should automatically retry
- **Default message** - Fallback message if none provided

---

## Complete Error Codes Table

| Code | HTTP Status | Retryable | Use Case |
|------|-------------|-----------|----------|
| `Internal` | 500 | No | Unexpected server errors |
| `BadRequest` | 400 | No | Malformed requests |
| `ValidationFailed` | 400 | No | Invalid input data |
| `Unauthorized` | 401 | No | Missing/invalid auth |
| `Forbidden` | 403 | No | Insufficient permissions |
| `NotFound` | 404 | No | Resource doesn't exist |
| `MethodNotAllowed` | 405 | No | Invalid HTTP method |
| `RequestTimeout` | 408 | Yes | Client timeout |
| `Conflict` | 409 | No | State conflict (duplicate) |
| `Gone` | 410 | No | Resource permanently deleted |
| `PayloadTooLarge` | 413 | No | Request body too large |
| `UnprocessableEntity` | 422 | No | Semantic validation failed |
| `RateLimited` | 429 | Yes | Too many requests |
| `Canceled` | 499 | No | Client canceled request |
| `Unavailable` | 503 | Yes | Service temporarily down |
| `Timeout` | 504 | Yes | Gateway timeout |
| `DownstreamError` | 502 | Yes | Upstream service failed |
| `DownstreamTimeout` | 504 | Yes | Upstream service timeout |

---

## Detailed Descriptions

### Generic Errors

#### Internal (500)
Unexpected server errors that aren't the client's fault.

**When to use:**
- Database connection failures
- Unexpected panics or bugs
- File system errors
- Memory allocation failures

**Example:**
```rust
Error::internal("Database connection pool exhausted")
```

#### BadRequest (400)
Malformed requests that can't be processed.

**When to use:**
- Invalid JSON syntax
- Missing required headers
- Malformed URLs
- Invalid content types

**Example:**
```rust
Error::bad_request("Invalid JSON in request body")
```

#### ValidationFailed (400)
Input data is well-formed but violates business rules.

**When to use:**
- Email format invalid
- Age out of range
- Field too long/short
- Invalid enum values

**Example:**
```rust
use error_envelope::validation;
use std::collections::HashMap;

let mut fields = HashMap::new();
fields.insert("email".to_string(), "Invalid email format".to_string());
fields.insert("age".to_string(), "Must be between 18 and 120".to_string());

let err = validation(fields);
```

---

### Auth Errors

#### Unauthorized (401)
Authentication required or credentials invalid.

**When to use:**
- Missing authentication token
- Expired token
- Invalid credentials
- Token signature verification failed

**Example:**
```rust
Error::unauthorized("JWT token expired")
```

#### Forbidden (403)
Authenticated but not authorized to access resource.

**When to use:**
- Insufficient permissions
- Wrong role
- Resource belongs to different user
- Feature not enabled for account

**Example:**
```rust
Error::forbidden("Admin role required")
```

---

### Resource Errors

#### NotFound (404)
Resource doesn't exist at the requested path.

**When to use:**
- User/record not in database
- Endpoint doesn't exist
- File not found
- Invalid ID

**Example:**
```rust
Error::not_found("User not found")
```

#### MethodNotAllowed (405)
HTTP method not supported for this endpoint.

**When to use:**
- POST to GET-only endpoint
- DELETE not allowed
- Method not implemented

**Example:**
```rust
Error::method_not_allowed("POST not allowed on this endpoint")
```

#### Gone (410)
Resource permanently deleted or removed.

**When to use:**
- Soft-deleted records
- Expired offers
- Deprecated endpoints
- Permanently removed content

**Example:**
```rust
Error::gone("This endpoint was deprecated in v2.0")
```

#### Conflict (409)
Request conflicts with current resource state.

**When to use:**
- Duplicate email registration
- Concurrent modification
- Version mismatch
- Unique constraint violation

**Example:**
```rust
Error::conflict("Email already registered")
```

---

### Request Errors

#### RequestTimeout (408)
Client-side timeout before request completed.

**When to use:**
- Client closed connection early
- Long-running client operations
- Upload timeouts

**Example:**
```rust
Error::request_timeout("Client closed connection")
```

**Note:** Retryable by default.

#### PayloadTooLarge (413)
Request body exceeds size limit.

**When to use:**
- File upload too large
- JSON payload exceeds limit
- Request headers too large

**Example:**
```rust
Error::payload_too_large("Upload exceeds 10MB limit")
```

#### UnprocessableEntity (422)
Request is well-formed but semantically invalid.

**When to use:**
- Business rule violations
- Dates in wrong order
- Amounts exceed limits
- Complex validation failures

**Example:**
```rust
Error::unprocessable_entity("Check-out date must be after check-in")
```

---

### Infrastructure Errors

#### RateLimited (429)
Too many requests from client.

**When to use:**
- Rate limit exceeded
- Quota exhausted
- Throttling applied

**Example:**
```rust
use std::time::Duration;

Error::rate_limited("Too many requests")
    .with_retry_after(Duration::from_secs(60))
```

**Note:** Retryable by default. Always include `retry_after` to tell clients when to retry.

#### Unavailable (503)
Service temporarily unavailable.

**When to use:**
- Maintenance mode
- Circuit breaker open
- Overloaded
- Starting up

**Example:**
```rust
Error::unavailable("Service in maintenance mode")
```

**Note:** Retryable by default.

#### Timeout (504)
Gateway or processing timeout.

**When to use:**
- Database query timeout
- HTTP client timeout
- Long-running operations
- Background job timeout

**Example:**
```rust
Error::timeout("Database query exceeded 30s timeout")
```

**Note:** Retryable by default.

#### Canceled (499)
Client canceled the request.

**When to use:**
- Client closed connection
- Request aborted
- User navigation away from page

**Example:**
```rust
Error::new(Code::Canceled, 499, "Request canceled by client")
```

**Note:** Not retryable (client intentionally canceled).

---

### Downstream Errors

#### DownstreamError (502)
Downstream service returned an error.

**When to use:**
- Payment gateway failures
- External API errors
- Third-party service issues
- Microservice failures

**Example:**
```rust
let payment_err = call_payment_service().await.unwrap_err();
Error::downstream("payments", payment_err)
```

**Note:** Retryable by default. Service name automatically added to details.

#### DownstreamTimeout (504)
Downstream service timed out.

**When to use:**
- External API timeout
- Microservice timeout
- Third-party service slow
- Network issues

**Example:**
```rust
let inventory_err = call_inventory_service().await.unwrap_err();
Error::downstream_timeout("inventory", inventory_err)
```

**Note:** Retryable by default. Service name automatically added to details.

---

## Choosing the Right Code

### Quick Reference

**Client made a mistake:**
- `BadRequest` - Malformed request
- `ValidationFailed` - Invalid data
- `Unauthorized` - Not authenticated
- `Forbidden` - Not authorized
- `NotFound` - Resource doesn't exist
- `Conflict` - Duplicate/state conflict

**Server had a problem:**
- `Internal` - Unexpected error
- `Unavailable` - Temporarily down
- `Timeout` - Operation took too long

**Downstream service failed:**
- `DownstreamError` - Service returned error
- `DownstreamTimeout` - Service timed out

**Rate limiting:**
- `RateLimited` - Too many requests (include retry_after)

---

## Retryable vs Non-Retryable

### Retryable by Default (Yes)

These errors are transient—retrying might succeed:
- `RequestTimeout` (408)
- `RateLimited` (429)
- `Unavailable` (503)
- `Timeout` (504)
- `DownstreamError` (502)
- `DownstreamTimeout` (504)

### Not Retryable by Default (No)

These errors are permanent—retrying won't help:
- `Internal` (500) - Server bug, not transient
- `BadRequest` (400) - Malformed request
- `ValidationFailed` (400) - Invalid data
- `Unauthorized` (401) - Auth failure
- `Forbidden` (403) - Permission denied
- `NotFound` (404) - Doesn't exist
- `MethodNotAllowed` (405) - Wrong method
- `Conflict` (409) - State conflict
- `Gone` (410) - Permanently deleted
- `PayloadTooLarge` (413) - Too large
- `UnprocessableEntity` (422) - Invalid semantics
- `Canceled` (499) - Client canceled

### Overriding Retryable Behavior

```rust
// Make Internal retryable (unusual, but possible)
Error::internal("Transient cache issue")
    .with_retryable(true);

// Make Timeout non-retryable (if you know it won't help)
Error::timeout("Query complexity limit exceeded")
    .with_retryable(false);
```

---

## See Also

- [README.md]README.md - Quick start and overview
- [ARCHITECTURE.md]ARCHITECTURE.md - Visual architecture guide with diagrams
- [examples/]examples/ - Runnable code examples
- [docs.rs/error-envelope]https://docs.rs/error-envelope - Full API documentation