# Error Handling in Feather
Feather provides flexible error handling mechanisms. This guide covers all approaches to managing errors in your application.
## Default Error Handling
By default, Feather catches all errors and returns a 500 Internal Server Error response to the client:
```rust,ignore
use feather::App;
fn main() {
let mut app = App::new();
// If any error occurs, Feather returns 500
app.get("/", middleware!(|_req, res, _ctx| {
std::fs::File::open("non-existingfile.txt")?; // This will send a 500 response to the client
next!()
}));
app.listen("127.0.0.1:5050");
}
```
The error is logged to `stderr` and a generic 500 response is sent to the client.
## Custom Error Handling
Set a custom error handler using `set_error_handler()`:
```rust,ignore
use feather::{App, ErrorHandler};
let error_handler: ErrorHandler = |err| {
eprintln!("Application error: {}", err);
// Return custom error response
None // Or Some(response) for custom responses
};
let mut app = App::new();
app.set_error_handler(error_handler);
```
## Handling Errors in Middleware
### Early Return on Error
Check conditions and return early:
```rust,ignore
res.send_text("User created");
next!()
}));
```
### Status Code Responses
Send error status codes:
```rust,ignore
res.set_status(403);
res.send_text("Access forbidden");
next!()
}));
### JSON Error Responses (with `json` feature)
```rust,ignore
#[cfg(feature = "json")]
"status": "success",
"data": null
}));
next!()
}));
```
### HTML Error Responses
```rust,ignore
```rust,ignore
if !body.contains("email") || !body.contains("password") {
res.set_status(400);
res.send_text("Missing required fields: email, password");
return next!();
}
// Check email format (simplified)
if !body.contains("@") {
res.set_status(400);
res.send_text("Invalid email format");
return next!();
}
res.send_text("Registration successful");
next!()
}));
```
## Error Middleware
Create middleware specifically for error handling:
```rust,ignore
// Global error handling middleware
app.use_middleware(middleware!(|req, res, ctx| {
// Check for problematic requests
if req.method.to_string() == "INVALID" {
res.set_status(400);
res.send_text("Invalid request method");
return Ok(MiddlewareResult::NextRoute);
}
next!()
}));
```
## Common HTTP Status Codes
### Client Errors (4xx)
- **400** - Bad Request (malformed request)
- **401** - Unauthorized (authentication required)
- **403** - Forbidden (authenticated but not allowed)
- **404** - Not Found (resource doesn't exist)
- **405** - Method Not Allowed (wrong HTTP method)
- **409** - Conflict (request conflicts with current state)
- **413** - Payload Too Large (request body too big)
- **415** - Unsupported Media Type (wrong content type)
- **422** - Unprocessable Entity (validation failed)
- **429** - Too Many Requests (rate limited)
### Server Errors (5xx)
- **500** - Internal Server Error (server-side error)
- **502** - Bad Gateway (upstream error)
- **503** - Service Unavailable (server temporarily unavailable)
- **504** - Gateway Timeout (upstream timeout)
## Error Recovery Patterns
### Graceful Degradation
```rust,ignore
res.send_text("Service temporarily unavailable, using cached data");
return next!();
}
};
res.send_text(format!("Data: {}", data));
next!()
}));
fn fetch_data() -> Result<String, Box<dyn std::error::Error>> {
// Simulate data fetching
Ok("data".to_string())
}
```
### Retry Logic
```rust,ignore
fn call_external_service(max_retries: usize) -> Result<String, String> {
for attempt in 1..=max_retries {
match try_service_call() {
Ok(result) => return Ok(result),
Err(e) if attempt < max_retries => {
eprintln!("Attempt {} failed: {}, retrying...", attempt, e);
std::thread::sleep(std::time::Duration::from_millis(100));
}
Err(e) => return Err(e),
}
}
unreachable!()
}
fn try_service_call() -> Result<String, String> {
Err("Connection timeout".to_string())
}
Include helpful context in error responses:
```rust,ignore
res.set_status(400);
res.send_text(format!(
"Invalid request to {}: Missing required fields",
path
));
return next!();
}
res.send_text("Updated successfully");
next!()
}));
fn validate_request(req: &feather_runtime::http::Request) -> bool {
!req.body.is_empty()
}
```
## Best Practices
1. **Always return a status code** - Help clients understand what happened
2. **Provide clear error messages** - Users should know what went wrong
3. **Log errors appropriately** - But don't expose internal details to clients
4. **Validate early** - Fail fast on invalid requests
5. **Use appropriate status codes** - Choose codes that match your error situation
6. **Handle all paths** - Ensure every code path has error handling
7. **Test error cases** - Don't just test the happy path